Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into ninjasarecum
# Conflicts: # tgstation.dme
This commit is contained in:
@@ -75,6 +75,9 @@
|
||||
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
|
||||
return
|
||||
if(occupant)
|
||||
to_chat(user, "<span class='boldnotice'>The VR Sleeper is already occupied!</span>")
|
||||
return
|
||||
close_machine(target)
|
||||
ui_interact(user)
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
|
||||
// #endif
|
||||
/datum/admins/proc/create_or_modify_area,
|
||||
/datum/admins/proc/fixcorruption,
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
/datum/admins/proc/view_refs,
|
||||
/datum/admins/proc/view_del_failures,
|
||||
#endif
|
||||
|
||||
@@ -2862,7 +2862,7 @@
|
||||
return
|
||||
|
||||
if(!CONFIG_GET(string/centcom_ban_db))
|
||||
to_chat(usr, "<span class='warning'>Centcom Galactic Ban DB is disabled!</span>")
|
||||
to_chat(usr, span_warning("Centcom Galactic Ban DB is disabled!"))
|
||||
return
|
||||
|
||||
var/ckey = href_list["centcomlookup"]
|
||||
@@ -2889,8 +2889,19 @@
|
||||
dat += "<center><b>0 bans detected for [ckey]</b></center>"
|
||||
else
|
||||
bans = json_decode(response["body"])
|
||||
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
|
||||
|
||||
//Ignore bans from non-whitelisted sources, if a whitelist exists
|
||||
var/list/valid_sources
|
||||
if(CONFIG_GET(string/centcom_source_whitelist))
|
||||
valid_sources = splittext(CONFIG_GET(string/centcom_source_whitelist), ",")
|
||||
dat += "<center><b>Bans detected for [ckey]</b></center>"
|
||||
else
|
||||
//Ban count is potentially inaccurate if they're using a whitelist
|
||||
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
|
||||
|
||||
for(var/list/ban in bans)
|
||||
if(valid_sources && !(ban["sourceName"] in valid_sources))
|
||||
continue
|
||||
dat += "<b>Server: </b> [sanitize(ban["sourceName"])]<br>"
|
||||
dat += "<b>RP Level: </b> [sanitize(ban["sourceRoleplayLevel"])]<br>"
|
||||
dat += "<b>Type: </b> [sanitize(ban["type"])]<br>"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
|
||||
GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
|
||||
@@ -102,29 +102,21 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
/datum/verb/legacy_find_refs()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set src in world
|
||||
|
||||
find_references_legacy(FALSE)
|
||||
|
||||
|
||||
/datum/proc/find_references_legacy(skip_alert)
|
||||
/datum/proc/find_references(skip_alert)
|
||||
running_find_references = type
|
||||
if(usr?.client)
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
log_reftracker("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
return
|
||||
|
||||
if(!skip_alert && alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
|
||||
if(!skip_alert && tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes")
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
@@ -134,92 +126,122 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
testing("Beginning search for references to a [type].")
|
||||
last_find_references = world.time
|
||||
log_reftracker("Beginning search for references to a [type].")
|
||||
|
||||
var/starting_time = world.time
|
||||
|
||||
//Time to search the whole game for our ref
|
||||
DoSearchVar(GLOB, "GLOB") //globals
|
||||
log_reftracker("Finished searching globals")
|
||||
|
||||
DoSearchVar(GLOB) //globals
|
||||
for(var/datum/thing in world) //atoms (don't beleive its lies)
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching atoms")
|
||||
|
||||
for(var/datum/thing) //datums
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "Datums -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching datums")
|
||||
|
||||
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
|
||||
for(var/client/thing) //clients
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching clients")
|
||||
|
||||
log_reftracker("Completed search for references to a [type].")
|
||||
|
||||
testing("Completed search for references to a [type].")
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
|
||||
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(!found_refs && SSgarbage.should_save_refs)
|
||||
found_refs = list()
|
||||
#endif
|
||||
|
||||
/datum/verb/qdel_then_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References"
|
||||
set src in world
|
||||
|
||||
qdel(src, TRUE) //force a qdel
|
||||
if(!running_find_references)
|
||||
find_references_legacy(TRUE)
|
||||
|
||||
|
||||
/datum/verb/qdel_then_if_fail_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References if GC failure"
|
||||
set src in world
|
||||
|
||||
qdel_and_find_ref_if_fail(src, TRUE)
|
||||
|
||||
|
||||
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64)
|
||||
if(usr?.client && !usr.client.running_find_references)
|
||||
return
|
||||
|
||||
if(!recursive_limit)
|
||||
log_reftracker("Recursion limit reached. [container_name]")
|
||||
return
|
||||
|
||||
if(istype(potential_container, /datum))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == last_find_references)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = last_find_references
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
if (varname == "vars")
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
|
||||
if(variable == src)
|
||||
testing("Found [type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]")
|
||||
|
||||
else if(islist(variable))
|
||||
DoSearchVar(variable, "[container_name] -> list", recursive_limit - 1)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
for(var/element_in_list in potential_container)
|
||||
if(element_in_list == src)
|
||||
testing("Found [type] \ref[src] in list [container_name].")
|
||||
|
||||
else if(element_in_list && !isnum(element_in_list) && normal && potential_container[element_in_list] == src)
|
||||
testing("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
|
||||
|
||||
else if(islist(element_in_list))
|
||||
DoSearchVar(element_in_list, "[container_name] -> list", recursive_limit - 1)
|
||||
|
||||
//Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
|
||||
if(istype(potential_container, /datum))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == search_time)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = search_time
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
|
||||
if(variable == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[varname] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in [datum_container.type]'s \ref[datum_container] [varname] var. [container_name]")
|
||||
continue
|
||||
|
||||
if(islist(variable))
|
||||
DoSearchVar(variable, "[container_name] \ref[datum_container] -> [varname] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
var/list/potential_cache = potential_container
|
||||
for(var/element_in_list in potential_cache)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
//Check normal entrys
|
||||
if(element_in_list == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in list [container_name].")
|
||||
continue
|
||||
|
||||
var/assoc_val = null
|
||||
if(!isnum(element_in_list) && normal)
|
||||
assoc_val = potential_cache[element_in_list]
|
||||
//Check assoc entrys
|
||||
if(assoc_val == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
|
||||
continue
|
||||
//We need to run both of these checks, since our object could be hiding in either of them
|
||||
//Check normal sublists
|
||||
if(islist(element_in_list))
|
||||
DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
|
||||
//Check assoc sublists
|
||||
if(islist(assoc_val))
|
||||
DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
|
||||
SSgarbage.reference_find_on_fail[REF(thing_to_del)] = TRUE
|
||||
qdel(thing_to_del, force)
|
||||
thing_to_del.qdel_and_find_ref_if_fail(force)
|
||||
|
||||
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
|
||||
SSgarbage.reference_find_on_fail["\ref[src]"] = TRUE
|
||||
qdel(src, force)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
usr.client.debug_variables(src)
|
||||
return
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
if(href_list[VV_HK_VIEW_REFERENCES])
|
||||
var/datum/D = locate(href_list[VV_HK_TARGET])
|
||||
if(!D)
|
||||
|
||||
@@ -111,6 +111,19 @@
|
||||
sigil_name = "Sigil of Submission"
|
||||
var/glow_type = /obj/effect/temp_visual/ratvar/sigil/submission
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(istype(AM, /obj/item/aicard))
|
||||
var/obj/item/aicard/cardy = AM
|
||||
if(!cardy.AI)
|
||||
return
|
||||
var/mob/living/silicon/ai/aiconvert = cardy.AI
|
||||
if(aiconvert.stat > stat_affected)
|
||||
return
|
||||
if(is_servant_of_ratvar(aiconvert) || !(aiconvert.mind || aiconvert.has_status_effect(STATUS_EFFECT_SIGILMARK)))
|
||||
return
|
||||
sigil_effects(aiconvert)
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
|
||||
var/turf/T = get_turf(src)
|
||||
var/has_sigil = FALSE
|
||||
|
||||
+4
-4
@@ -253,7 +253,7 @@
|
||||
purpose_fulfilled = TRUE
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 125)
|
||||
sound_to_playing_players(volume = 100, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/ratvar_rises.ogg')) //End the sounds
|
||||
sound_to_playing_players('sound/effects/ratvar_rises.ogg', 90, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
sleep(125)
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
|
||||
@@ -318,19 +318,19 @@
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
if(!second_sound_played)
|
||||
sound_to_playing_players('sound/magic/clockwork/invoke_general.ogg', 30, FALSE)
|
||||
sound_to_playing_players(volume = 10, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_charging.ogg', 10, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
second_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_charging"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
if(!third_sound_played)
|
||||
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_active.ogg', 30, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
third_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_active"
|
||||
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
|
||||
if(!fourth_sound_played)
|
||||
sound_to_playing_players(volume = 70, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_closing.ogg', 70, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
fourth_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_closing"
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
if((IS_HERETIC(local_user) || IS_HERETIC_MONSTER(local_user)) && HAS_TRAIT(src,TRAIT_NODROP))
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
|
||||
|
||||
for(var/mob/living/carbon/human/human_in_range in spiral_range(9,local_user))
|
||||
for(var/mob/living/carbon/human/human_in_range in viewers(9,local_user))
|
||||
if(IS_HERETIC(human_in_range) || IS_HERETIC_MONSTER(human_in_range))
|
||||
continue
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "Prison Escapee"
|
||||
uniform = /obj/item/clothing/under/rank/prisoner
|
||||
shoes = /obj/item/clothing/shoes/sneakers/orange
|
||||
r_pocket = /obj/item/kitchen/knife
|
||||
r_pocket = /obj/item/kitchen/knife/shiv
|
||||
|
||||
/datum/outfit/prisoner/post_equip(mob/living/carbon/human/H, visualsOnly=FALSE)
|
||||
if(visualsOnly)
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
healable = 0
|
||||
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
|
||||
obj_damage = 50
|
||||
melee_damage_lower = 22.5 // reduced from 30 to 22.5 with wounds since they get big buffs to slicing wounds
|
||||
melee_damage_upper = 22.5
|
||||
wound_bonus = -10
|
||||
melee_damage_lower = 30 // buffed back to 30, the wounds don't do much
|
||||
melee_damage_upper = 30
|
||||
wound_bonus = 0
|
||||
bare_wound_bonus = 0
|
||||
sharpness = SHARP_EDGED
|
||||
see_in_dark = 8
|
||||
@@ -49,17 +49,13 @@
|
||||
Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \
|
||||
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. \
|
||||
You gain strength the more attacks you land on live humanoids, though this resets when you return to the blood zone. You can also \
|
||||
launch a devastating slam attack with ctrl+shift+click, capable of smashing bones in one strike.</B>"
|
||||
launch a devastating slam attack, capable of smashing bones in one strike.</B>"
|
||||
|
||||
loot = list(/obj/effect/decal/cleanable/blood, \
|
||||
/obj/effect/decal/cleanable/blood/innards, \
|
||||
/obj/item/organ/heart/demon)
|
||||
del_on_death = 1
|
||||
deathmessage = "screams in anger as it collapses into a puddle of viscera!"
|
||||
// How long it takes for the alt-click slam attack to come off cooldown
|
||||
var/slam_cooldown_time = 45 SECONDS
|
||||
// The actual instance var for the cooldown
|
||||
var/slam_cooldown = 0
|
||||
// How many times we have hit humanoid targets since we last bloodcrawled, scaling wounding power
|
||||
var/current_hitstreak = 0
|
||||
// How much both our wound_bonus and bare_wound_bonus go up per hitstreak hit
|
||||
@@ -70,37 +66,56 @@
|
||||
var/list/consumed_mobs = list()
|
||||
//buffs only happen when hearts are eaten, so this needs to be kept track separately
|
||||
var/consumed_buff = 0
|
||||
//slam mode for action button
|
||||
var/slam_mode = FALSE
|
||||
var/datum/action/cooldown/slam
|
||||
|
||||
/mob/living/simple_animal/slaughter/Initialize()
|
||||
..()
|
||||
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
|
||||
AddSpell(bloodspell)
|
||||
slam = new /datum/action/cooldown/slam
|
||||
slam.Grant(src)
|
||||
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
|
||||
bloodspell.phased = TRUE
|
||||
|
||||
/mob/living/simple_animal/slaughter/CtrlShiftClickOn(atom/A)
|
||||
if(!isliving(A))
|
||||
return ..()
|
||||
if(slam_cooldown + slam_cooldown_time > world.time)
|
||||
to_chat(src, "<span class='warning'>Your slam ability is still on cooldown!</span>")
|
||||
return
|
||||
if(!isopenturf(loc))
|
||||
to_chat(src, "<span class='warning'>You need to be on open flooring to do that!")
|
||||
return
|
||||
/datum/action/cooldown/slam
|
||||
name = "Slaughter Slam"
|
||||
desc = "Launch enemies and break bones in one strike."
|
||||
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
||||
background_icon_state = "bg_demon"
|
||||
button_icon_state = "slam"
|
||||
cooldown_time = 45 SECONDS
|
||||
|
||||
face_atom(A)
|
||||
var/mob/living/victim = A
|
||||
victim.take_bodypart_damage(brute=20, wound_bonus=wound_bonus) // don't worry, there's more punishment when they hit something
|
||||
visible_message("<span class='danger'>[src] slams into [victim] with monstrous strength!</span>", "<span class='danger'>You slam into [victim] with monstrous strength!</span>", ignored_mobs=victim)
|
||||
to_chat(victim, "<span class='userdanger'>[src] slams into you with monstrous strength, sending you flying like a ragdoll!</span>")
|
||||
var/turf/yeet_target = get_edge_target_turf(victim, dir)
|
||||
victim.throw_at(yeet_target, 10, 5, src)
|
||||
slam_cooldown = world.time
|
||||
log_combat(src, victim, "slaughter slammed")
|
||||
/datum/action/cooldown/slam/Trigger()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/mob/living/simple_animal/slaughter/user = owner
|
||||
user.slam_mode = !user.slam_mode
|
||||
to_chat(user, user.slam_mode ? "Ready to slam!" : "Maybe not now.")
|
||||
|
||||
/mob/living/simple_animal/slaughter/UnarmedAttack(atom/A, proximity)
|
||||
if(iscarbon(A))
|
||||
var/mob/living/carbon/target = A
|
||||
if(slam_mode)
|
||||
if(!isopenturf(loc))
|
||||
to_chat(src, "<span class='warning'>You need to be on open flooring to do that!")
|
||||
return
|
||||
face_atom(A)
|
||||
var/mob/living/victim = A
|
||||
var/body_pick = pick(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG)
|
||||
var/datum/wound/blunt/critical/wound_major = new
|
||||
var/obj/item/bodypart/body_wound = victim.get_bodypart(body_pick)
|
||||
wound_major.apply_wound(body_wound)
|
||||
visible_message("<span class='danger'>[src] slams into [victim] with monstrous strength!</span>", "<span class='danger'>You slam into [victim] with monstrous strength!</span>", ignored_mobs=victim)
|
||||
to_chat(victim, "<span class='userdanger'>[src] slams into you with monstrous strength, sending you flying like a ragdoll!</span>")
|
||||
var/turf/yeet_target = get_edge_target_turf(victim, dir)
|
||||
victim.throw_at(yeet_target, 10, 14, src)
|
||||
slam_mode = FALSE
|
||||
slam.StartCooldown()
|
||||
log_combat(src, victim, "slaughter slammed")
|
||||
|
||||
if(target.stat != DEAD && target.mind && current_hitstreak < wound_bonus_hitstreak_max)
|
||||
current_hitstreak++
|
||||
wound_bonus += wound_bonus_per_hit
|
||||
@@ -129,13 +144,12 @@
|
||||
/mob/living/simple_animal/slaughter/proc/release_victims()
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
T = find_safe_turf()
|
||||
for(var/mob/living/M in consumed_mobs)
|
||||
if(!M)
|
||||
continue
|
||||
var/turf/T = find_safe_turf()
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
M.forceMove(T)
|
||||
|
||||
/mob/living/simple_animal/slaughter/proc/refresh_consumed_buff()
|
||||
@@ -263,12 +277,12 @@
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
T = find_safe_turf()
|
||||
for(var/mob/living/M in consumed_mobs)
|
||||
if(!M)
|
||||
continue
|
||||
var/turf/T = find_safe_turf()
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
continue
|
||||
M.forceMove(T)
|
||||
if(M.revive(full_heal = TRUE, admin_revive = TRUE))
|
||||
M.grab_ghost(force = TRUE)
|
||||
|
||||
@@ -45,5 +45,5 @@ GLOBAL_LIST_EMPTY(traitor_classes)
|
||||
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
|
||||
// Any effects that need to be cleaned up if traitor class is being swapped.
|
||||
|
||||
/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
|
||||
/datum/traitor_class/proc/on_process(datum/antagonist/traitor/T)
|
||||
// only for processing traitor classes; runs once an SSprocessing tick
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
/datum/asset/simple/tgui_common
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-common.bundle.js" = 'tgui/public/tgui-common.bundle.js',
|
||||
"tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui.bundle.js" = 'tgui/public/tgui.bundle.js',
|
||||
"tgui.bundle.css" = 'tgui/public/tgui.bundle.css',
|
||||
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
|
||||
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui_panel
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-panel.bundle.js" = 'tgui/public/tgui-panel.bundle.js',
|
||||
"tgui-panel.bundle.css" = 'tgui/public/tgui-panel.bundle.css',
|
||||
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
|
||||
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/headers
|
||||
@@ -168,10 +168,12 @@
|
||||
|
||||
/datum/asset/simple/namespaced/tgfont
|
||||
assets = list(
|
||||
"tgfont.eot" = 'tgui/packages/tgfont/dist/tgfont.eot',
|
||||
"tgfont.woff2" = 'tgui/packages/tgfont/dist/tgfont.woff2',
|
||||
"tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"),
|
||||
"tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"),
|
||||
)
|
||||
parents = list(
|
||||
"tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"),
|
||||
)
|
||||
parents = list("tgfont.css" = 'tgui/packages/tgfont/dist/tgfont.css')
|
||||
|
||||
/datum/asset/spritesheet/chat
|
||||
name = "chat"
|
||||
|
||||
@@ -157,6 +157,8 @@
|
||||
id = GAS_METHANE
|
||||
specific_heat = 30
|
||||
name = "Methane"
|
||||
powerloss_inhibition = 1
|
||||
heat_resistance = 3
|
||||
breath_results = GAS_METHYL_BROMIDE
|
||||
fire_products = list(GAS_CO2 = 1, GAS_H2O = 2)
|
||||
fire_burn_rate = 0.5
|
||||
@@ -177,6 +179,8 @@
|
||||
id = GAS_METHYL_BROMIDE
|
||||
specific_heat = 42
|
||||
name = "Methyl Bromide"
|
||||
powermix = 1
|
||||
heat_penalty = -1
|
||||
flags = GAS_FLAG_DANGEROUS
|
||||
breath_alert_info = list(
|
||||
not_enough_alert = list(
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
if(!blocks_air)
|
||||
air = new(2500,src)
|
||||
air.copy_from_turf(src)
|
||||
if(planetary_atmos && !(initial_gas_mix in SSair.planetary))
|
||||
var/datum/gas_mixture/mix = new
|
||||
mix.parse_gas_string(initial_gas_mix)
|
||||
mix.mark_immutable()
|
||||
SSair.planetary[initial_gas_mix] = mix
|
||||
update_air_ref(planetary_atmos ? 1 : 2)
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -261,6 +261,8 @@ we use a hook instead
|
||||
return 1
|
||||
|
||||
/datum/gas_mixture/parse_gas_string(gas_string)
|
||||
gas_string = SSair.preprocess_gas_string(gas_string)
|
||||
|
||||
var/list/gas = params2list(gas_string)
|
||||
if(gas["TEMP"])
|
||||
var/temp = text2num(gas["TEMP"])
|
||||
|
||||
@@ -648,7 +648,7 @@
|
||||
//Replace miasma with oxygen
|
||||
var/cleaned_air = min(air.get_moles(GAS_MIASMA), 20 + (air.return_temperature() - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20)
|
||||
air.adjust_moles(GAS_MIASMA, -cleaned_air)
|
||||
air.adjust_moles(GAS_O2, cleaned_air)
|
||||
air.adjust_moles(GAS_METHANE, cleaned_air)
|
||||
|
||||
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
|
||||
air.set_temperature(air.return_temperature() + cleaned_air * 0.002)
|
||||
|
||||
@@ -75,6 +75,9 @@
|
||||
/obj/machinery/atmospherics/pipe/setPipenet(datum/pipeline/P)
|
||||
parent = P
|
||||
|
||||
/obj/machinery/atmospherics/pipe/zap_act(power, zap_flags)
|
||||
return 0 // they're not really machines in the normal sense, probably shouldn't explode
|
||||
|
||||
/obj/machinery/atmospherics/pipe/Destroy()
|
||||
QDEL_NULL(parent)
|
||||
|
||||
|
||||
@@ -172,15 +172,13 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
/// bumper object, the thing that starts actual teleport
|
||||
var/obj/effect/gateway_portal_bumper/portal
|
||||
/// Visual object for handling the viscontents
|
||||
/// DISABLED DUE TO BYOND BUG CAUSING STACK OVERFLOWS OF ANY HUMAN INSTANTIATION NEAR AN ACTIVATED GATEWAY.
|
||||
/// Probably due to it referencing each other through the gateway (there's a deep loop, maybe BYOND isn't catching something when it usually would)
|
||||
// var/obj/effect/gateway_portal_effect/portal_visuals
|
||||
var/obj/effect/gateway_portal_effect/portal_visuals
|
||||
|
||||
/obj/machinery/gateway/Initialize()
|
||||
generate_destination()
|
||||
update_icon()
|
||||
// portal_visuals = new
|
||||
// vis_contents += portal_visuals
|
||||
portal_visuals = new
|
||||
vis_contents += portal_visuals
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gateway/proc/generate_destination()
|
||||
@@ -197,7 +195,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
if(use_power == ACTIVE_POWER_USE)
|
||||
use_power = IDLE_POWER_USE
|
||||
update_icon()
|
||||
// portal_visuals.reset_visuals()
|
||||
portal_visuals.reset_visuals()
|
||||
|
||||
/obj/machinery/gateway/process()
|
||||
if((stat & (NOPOWER)) && use_power)
|
||||
@@ -205,12 +203,6 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
deactivate()
|
||||
return
|
||||
|
||||
/obj/machinery/gateway/update_icon_state()
|
||||
if(target)
|
||||
icon_state = "on_old"
|
||||
else
|
||||
icon_state = "portal_frame"
|
||||
|
||||
/obj/machinery/gateway/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
|
||||
return
|
||||
|
||||
@@ -223,7 +215,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
return
|
||||
target = D
|
||||
target.activate(destination)
|
||||
// portal_visuals.setup_visuals(target)
|
||||
portal_visuals.setup_visuals(target)
|
||||
generate_bumper()
|
||||
use_power = ACTIVE_POWER_USE
|
||||
update_icon()
|
||||
@@ -365,6 +357,8 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
|
||||
animate(get_filter("portal_ripple"), time = 1.3 SECONDS, loop = -1, easing = LINEAR_EASING, radius = 32)
|
||||
|
||||
var/turf/center_turf = our_destination.get_target_turf()
|
||||
/// DISABLED DUE TO BYOND BUG CAUSING STACK OVERFLOWS OF ANY HUMAN INSTANTIATION NEAR AN ACTIVATED GATEWAY.
|
||||
/// Probably due to it referencing each other through the gateway (there's a deep loop, maybe BYOND isn't catching something when it usually would)
|
||||
//var/turf/center_turf = our_destination.get_target_turf()
|
||||
|
||||
vis_contents += block(locate(center_turf.x - 1, center_turf.y - 1, center_turf.z), locate(center_turf.x + 1, center_turf.y + 1, center_turf.z))
|
||||
//vis_contents += block(locate(center_turf.x - 1, center_turf.y - 1, center_turf.z), locate(center_turf.x + 1, center_turf.y + 1, center_turf.z))
|
||||
|
||||
@@ -59,6 +59,11 @@
|
||||
unit_name = "alien hide"
|
||||
export_types = list(/obj/item/stack/sheet/animalhide/xeno)
|
||||
|
||||
/datum/export/stack/licenseplate
|
||||
cost = 25
|
||||
unit_name = "license plate"
|
||||
export_types = list(/obj/item/stack/license_plates/filled)
|
||||
|
||||
// Common materials.
|
||||
// For base materials, see materials.dm
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
cost = 1500
|
||||
contains = list(/obj/item/toy/plush/beeplushie)
|
||||
|
||||
/datum/supply_pack/goody/dyespray
|
||||
name = "Hair Dye Spray"
|
||||
desc = "A cool spray to dye your hair with awesome colors!"
|
||||
cost = PAYCHECK_EASY * 2
|
||||
contains = list(/obj/item/dyespray)
|
||||
|
||||
/datum/supply_pack/goody/beach_ball
|
||||
name = "Beach Ball"
|
||||
desc = "The simple beach ball is one of Nanotrasen's most popular products. 'Why do we make beach balls? Because we can! (TM)' - Nanotrasen"
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
cost = 300 //thrice their export value
|
||||
contains = list(/obj/item/stack/sheet/cardboard/fifty)
|
||||
|
||||
/datum/supply_pack/materials/license50
|
||||
name = "50 Empty License Plates"
|
||||
desc = "Create a bunch of boxes."
|
||||
cost = 1000 // 50 * 25 + 700 - 1000 = 950 credits profit
|
||||
contains = list(/obj/item/stack/license_plates/empty/fifty)
|
||||
crate_name = "empty license plate crate"
|
||||
|
||||
/datum/supply_pack/materials/glass50
|
||||
crate_type = /obj/structure/closet/secure_closet/cargo
|
||||
name = "50 Glass Sheets"
|
||||
|
||||
@@ -60,6 +60,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
//autocorrected this round, not that you'd need to check that.
|
||||
|
||||
var/UI_style = null
|
||||
var/outline_enabled = TRUE
|
||||
var/outline_color = COLOR_BLUE_GRAY
|
||||
var/buttons_locked = FALSE
|
||||
var/hotkeys = FALSE
|
||||
|
||||
@@ -117,6 +119,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/hair_color = "000000" //Hair color
|
||||
var/facial_hair_style = "Shaved" //Face hair type
|
||||
var/facial_hair_color = "000000" //Facial hair color
|
||||
var/grad_style //Hair gradient style
|
||||
var/grad_color = "FFFFFF" //Hair gradient color
|
||||
var/skin_tone = "caucasian1" //Skin color
|
||||
var/use_custom_skin_tone = FALSE
|
||||
var/left_eye_color = "000000" //Eye color
|
||||
@@ -503,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<a href='?_src_=prefs;preference=previous_facehair_style;task=input'><</a> <a href='?_src_=prefs;preference=next_facehair_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[facial_hair_color];'> </span> <a href='?_src_=prefs;preference=facial;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "<h3>Hair Gradient</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=grad_style;task=input'>[grad_style]</a>"
|
||||
dat += "<a href='?_src_=prefs;preference=previous_grad_style;task=input'><</a> <a href='?_src_=prefs;preference=next_grad_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[grad_color];'> </span> <a href='?_src_=prefs;preference=grad_color;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "</td>"
|
||||
//Mutant stuff
|
||||
var/mutant_category = 0
|
||||
@@ -774,6 +784,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
|
||||
dat += "<h2>General Settings</h2>"
|
||||
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
|
||||
dat += "<b>Outline:</b> <a href='?_src_=prefs;preference=outline_enabled'>[outline_enabled ? "Enabled" : "Disabled"]</a><br>"
|
||||
dat += "<b>Outline Color:</b> <span style='border:1px solid #161616; background-color: [outline_color];'> </span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
|
||||
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
|
||||
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
|
||||
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
|
||||
@@ -1707,6 +1719,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if("previous_facehair_style")
|
||||
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
|
||||
|
||||
if("grad_color")
|
||||
var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null
|
||||
if(new_grad_color)
|
||||
grad_color = sanitize_hexcolor(new_grad_color, 6)
|
||||
|
||||
if("grad_style")
|
||||
var/new_grad_style
|
||||
new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list
|
||||
if(new_grad_style)
|
||||
grad_style = new_grad_style
|
||||
|
||||
if("next_grad_style")
|
||||
grad_style = next_list_item(grad_style, GLOB.hair_gradients_list)
|
||||
|
||||
if("previous_grad_style")
|
||||
grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list)
|
||||
|
||||
if("cycle_bg")
|
||||
bgstate = next_list_item(bgstate, bgstate_options)
|
||||
|
||||
@@ -2706,6 +2735,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
buttons_locked = !buttons_locked
|
||||
if("tgui_fancy")
|
||||
tgui_fancy = !tgui_fancy
|
||||
if("outline_enabled")
|
||||
outline_enabled = !outline_enabled
|
||||
if("outline_color")
|
||||
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
|
||||
if(pickedOutlineColor)
|
||||
outline_color = pickedOutlineColor
|
||||
if("tgui_lock")
|
||||
tgui_lock = !tgui_lock
|
||||
if("winflash")
|
||||
@@ -3006,6 +3041,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.dna.skin_tone_override = use_custom_skin_tone ? skin_tone : null
|
||||
character.hair_style = hair_style
|
||||
character.facial_hair_style = facial_hair_style
|
||||
character.grad_style = grad_style
|
||||
character.grad_color = grad_color
|
||||
character.underwear = underwear
|
||||
|
||||
character.saved_underwear = underwear
|
||||
@@ -3070,6 +3107,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(additional_language && additional_language != "None")
|
||||
var/language_entry = GLOB.roundstart_languages[additional_language]
|
||||
if(language_entry)
|
||||
character.additional_language = language_entry
|
||||
character.grant_language(language_entry, TRUE, TRUE)
|
||||
|
||||
//limb stuff, only done when initially spawning in
|
||||
|
||||
@@ -45,6 +45,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
|
||||
force_reset_keybindings_direct(TRUE)
|
||||
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
|
||||
if(current_version < 30)
|
||||
outline_enabled = TRUE
|
||||
outline_color = COLOR_BLUE_GRAY
|
||||
|
||||
/datum/preferences/proc/update_character(current_version, savefile/S)
|
||||
if(current_version < 19)
|
||||
@@ -377,6 +380,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["ooccolor"] >> ooccolor
|
||||
S["lastchangelog"] >> lastchangelog
|
||||
S["UI_style"] >> UI_style
|
||||
S["outline_color"] >> outline_color
|
||||
S["outline_enabled"] >> outline_enabled
|
||||
S["hotkeys"] >> hotkeys
|
||||
S["chat_on_map"] >> chat_on_map
|
||||
S["max_chat_length"] >> max_chat_length
|
||||
@@ -555,6 +560,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["ooccolor"], ooccolor)
|
||||
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
||||
WRITE_FILE(S["UI_style"], UI_style)
|
||||
WRITE_FILE(S["outline_enabled"], outline_enabled)
|
||||
WRITE_FILE(S["outline_color"], outline_color)
|
||||
WRITE_FILE(S["hotkeys"], hotkeys)
|
||||
WRITE_FILE(S["chat_on_map"], chat_on_map)
|
||||
WRITE_FILE(S["max_chat_length"], max_chat_length)
|
||||
@@ -676,6 +683,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["skin_tone"] >> skin_tone
|
||||
S["hair_style_name"] >> hair_style
|
||||
S["facial_style_name"] >> facial_hair_style
|
||||
S["grad_style"] >> grad_style
|
||||
S["grad_color"] >> grad_color
|
||||
S["underwear"] >> underwear
|
||||
S["undie_color"] >> undie_color
|
||||
S["undershirt"] >> undershirt
|
||||
@@ -868,6 +877,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
|
||||
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
|
||||
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
|
||||
grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None")
|
||||
grad_color = sanitize_hexcolor(grad_color, 6, FALSE)
|
||||
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
|
||||
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
|
||||
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
|
||||
@@ -1037,6 +1048,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["skin_tone"] , skin_tone)
|
||||
WRITE_FILE(S["hair_style_name"] , hair_style)
|
||||
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
|
||||
WRITE_FILE(S["grad_style"] , grad_style)
|
||||
WRITE_FILE(S["grad_color"] , grad_color)
|
||||
WRITE_FILE(S["underwear"] , underwear)
|
||||
WRITE_FILE(S["undie_color"] , undie_color)
|
||||
WRITE_FILE(S["undershirt"] , undershirt)
|
||||
|
||||
@@ -207,6 +207,10 @@
|
||||
message_admins("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) [is_special_character(src) ? "(ANTAG!) " : ""][ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
|
||||
|
||||
/mob/living/proc/canSuicide()
|
||||
var/area/A = get_area(src)
|
||||
if(A.area_flags & BLOCK_SUICIDE)
|
||||
to_chat(src, span_warning("You can't commit suicide here! You can ghost if you'd like."))
|
||||
return FALSE
|
||||
if(!CONFIG_GET(flag/suicide_allowed))
|
||||
to_chat(src, "Suicide is not enabled in the config.")
|
||||
return FALSE
|
||||
@@ -214,11 +218,11 @@
|
||||
if(CONSCIOUS)
|
||||
return TRUE
|
||||
if(SOFT_CRIT)
|
||||
to_chat(src, "You can't commit suicide while in a critical condition!")
|
||||
to_chat(src, span_warning("You can't commit suicide while in a critical condition!"))
|
||||
if(UNCONSCIOUS)
|
||||
to_chat(src, "You need to be conscious to commit suicide!")
|
||||
to_chat(src, span_warning("You need to be conscious to commit suicide!"))
|
||||
if(DEAD)
|
||||
to_chat(src, "You're already dead!")
|
||||
to_chat(src, span_warning("You're already dead!"))
|
||||
return
|
||||
|
||||
/mob/living/carbon/canSuicide()
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
icon_state = "clown"
|
||||
item_state = "clown_hat"
|
||||
dye_color = "clown"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
@@ -131,6 +132,7 @@
|
||||
clothing_flags = ALLOWINTERNALS
|
||||
icon_state = "mime"
|
||||
item_state = "mime"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
back = /obj/item/storage/backpack/captain
|
||||
belt = /obj/item/storage/belt/security/full
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert
|
||||
glasses = /obj/item/clothing/glasses/thermal/eyepatch
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
/datum/outfit/ert/commander/alert/red
|
||||
name = "ERT Commander - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1)
|
||||
@@ -71,7 +71,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
belt = /obj/item/storage/belt/security/full
|
||||
back = /obj/item/storage/backpack/security
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun/stun=1,\
|
||||
@@ -91,7 +91,7 @@
|
||||
name = "ERT Security - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/sec
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
/datum/outfit/ert/security/alert/red
|
||||
name = "ERT Security - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
@@ -114,7 +114,7 @@
|
||||
back = /obj/item/storage/backpack/satchel/med
|
||||
belt = /obj/item/storage/belt/medical
|
||||
r_hand = /obj/item/storage/firstaid/regular
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -135,7 +135,7 @@
|
||||
name = "ERT Medic - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/med
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -144,7 +144,7 @@
|
||||
|
||||
/datum/outfit/ert/medic/alert/red
|
||||
name = "ERT Medic - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
|
||||
@@ -161,7 +161,7 @@
|
||||
belt = /obj/item/storage/belt/utility/full
|
||||
l_pocket = /obj/item/rcd_ammo/large
|
||||
r_hand = /obj/item/storage/firstaid/regular
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -181,7 +181,7 @@
|
||||
name = "ERT Engineer - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/engi
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
/datum/outfit/ert/engineer/alert/red
|
||||
name = "ERT Engineer - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
|
||||
@@ -260,7 +260,7 @@
|
||||
name = "Inquisition Commander"
|
||||
r_hand = /obj/item/nullrod/scythe/talking/chainsword
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/storage/box/handcuffs=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun/stun=1,
|
||||
@@ -281,7 +281,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/melee/baton/loaded=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1,
|
||||
@@ -307,7 +307,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/health
|
||||
back = /obj/item/storage/backpack/cultpack
|
||||
belt = /obj/item/storage/belt/soulstone
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/nullrod=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1,
|
||||
@@ -319,7 +319,7 @@
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
belt = /obj/item/storage/belt/soulstone/full/chappy
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/grenade/chem_grenade/holy=1,
|
||||
/obj/item/nullrod=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
head = /obj/item/clothing/head/helmet/space/plasmaman/security/hos
|
||||
uniform = /obj/item/clothing/under/plasmaman/security/hos
|
||||
|
||||
/datum/outfit/plasmaman/prisoner
|
||||
name = "Prisoner Plasmaman"
|
||||
|
||||
head = /obj/item/clothing/head/helmet/space/plasmaman/prisoner
|
||||
uniform = /obj/item/clothing/under/plasmaman/prisoner
|
||||
|
||||
/datum/outfit/plasmaman/cargo
|
||||
name = "Cargo Plasmaman"
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
id = /obj/item/card/id/syndicate/locked_banking
|
||||
belt = /obj/item/gun/ballistic/automatic/pistol
|
||||
l_pocket = /obj/item/paper/fluff/vr/fluke_ops
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/kitchen/knife/combat/survival)
|
||||
starting_funds = 0 //Should be operating, not shopping.
|
||||
|
||||
|
||||
@@ -237,12 +237,28 @@
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
hardsuit_type = "mining_goliath"
|
||||
if(amount == maxamount)
|
||||
hardsuit_type = "mining_goliath_full"
|
||||
icon_state = "hardsuit[on]-[hardsuit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.head == src)
|
||||
wearer.update_inv_head()
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/mining
|
||||
icon_state = "hardsuit-mining"
|
||||
name = "mining hardsuit"
|
||||
desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating for wildlife encounters."
|
||||
item_state = "mining_hardsuit"
|
||||
hardsuit_type = "mining"
|
||||
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF
|
||||
armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75, "wound" = 15)
|
||||
@@ -254,6 +270,21 @@
|
||||
/obj/item/clothing/suit/space/hardsuit/mining/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
hardsuit_type = "mining_goliath"
|
||||
if(amount == maxamount)
|
||||
hardsuit_type = "mining_goliath_full"
|
||||
icon_state = "hardsuit-[hardsuit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.wear_suit == src)
|
||||
wearer.update_inv_wear_suit()
|
||||
|
||||
//Syndicate hardsuit
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/syndi
|
||||
|
||||
@@ -169,6 +169,10 @@
|
||||
icon_state = "hos_envirohelm"
|
||||
item_state = "hos_envirohelm"
|
||||
|
||||
/obj/item/clothing/head/helmet/space/plasmaman/prisoner
|
||||
name = "prisoner's plasma envirosuit helmet"
|
||||
desc = "A plasmaman containment helmet for prisoners."
|
||||
|
||||
/obj/item/clothing/head/helmet/space/plasmaman/medical
|
||||
name = "medical's plasma envirosuit helmet"
|
||||
desc = "An envriohelmet designed for plasmaman medical doctors, having two stripes down it's length to denote as much."
|
||||
|
||||
@@ -136,17 +136,24 @@
|
||||
icon_state = "syndievest"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/capcarapace/alt
|
||||
/obj/item/clothing/suit/toggle/captains_parade
|
||||
name = "captain's parade jacket"
|
||||
desc = "For when an armoured vest isn't fashionable enough."
|
||||
icon_state = "capformal"
|
||||
item_state = "capspacesuit"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90, "wound" = 10)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/captains_parade/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
/obj/item/clothing/suit/armor/riot
|
||||
name = "riot suit"
|
||||
desc = "A suit of semi-flexible polycarbonate body armor with heavy padding to protect against melee attacks. Helps the wearer resist shoving in close quarters."
|
||||
icon_state = "swat"
|
||||
item_state = "swat_suit"
|
||||
icon_state = "riot"
|
||||
item_state = "riot"
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
@@ -319,3 +326,29 @@
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
|
||||
armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50, "wound" = 10)
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal
|
||||
name = "\improper CentCom formal coat"
|
||||
desc = "A stylish coat given to CentCom Commanders. Perfect for sending ERTs to suicide missions with style!"
|
||||
icon_state = "centcom_formal"
|
||||
item_state = "centcom"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 35, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 35, "bio" = 10, "rad" = 10, "fire" = 10, "acid" = 60)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/hos/hos_formal
|
||||
name = "\improper Head of Security's parade jacket"
|
||||
desc = "For when an armoured vest isn't fashionable enough."
|
||||
icon_state = "hosformal"
|
||||
item_state = "hostrench"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 10)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/hos/hos_formal/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
@@ -446,6 +446,42 @@
|
||||
icon_state = "flannel_brown"
|
||||
item_state = "flannel_brown"
|
||||
|
||||
/obj/item/clothing/suit/jacket/purplehoodie
|
||||
name = "purple hoodie"
|
||||
desc = "A soft purple hoodie with a TailorCo brand on the tag."
|
||||
icon_state = "purplehoodie"
|
||||
item_state = "purplehoodie"
|
||||
|
||||
/obj/item/clothing/suit/jacket/bluehoodie
|
||||
name = "blue hoodie"
|
||||
desc = "A soft blue hoodie with a TailorCo brand on the tag."
|
||||
icon_state = "bluehoodie"
|
||||
item_state = "bluehoodie"
|
||||
|
||||
/obj/item/clothing/suit/jacket/heartcoat
|
||||
name = "heart coat"
|
||||
desc = "A soft winter coat with a TailorCo brand on the tag."
|
||||
icon_state = "heartcoat"
|
||||
item_state = "heartcoat"
|
||||
|
||||
/obj/item/clothing/suit/jacket/gothiccoat
|
||||
name = "long black jacket"
|
||||
desc = "A rugged looking coat with a TailorCo brand on the tag."
|
||||
icon_state = "gothic_coat"
|
||||
item_state = "gothic_coat"
|
||||
|
||||
/obj/item/clothing/suit/jacket/gothicshirt
|
||||
name = "black shirt with cuffs"
|
||||
desc = "A black shirt with a collar and cuffs in a gothic style. A TailorCo brand is listed on the tag."
|
||||
icon_state = "gothic_shirt"
|
||||
item_state = "gothic_shirt"
|
||||
|
||||
/obj/item/clothing/suit/jacket/gothicshirtcross
|
||||
name = "elegant black shirt"
|
||||
desc = "A black shirt with finely woven cross on the back. A TailorCo brand is listed on the tag."
|
||||
icon_state = "gothic_shirtcross"
|
||||
item_state = "gothic_shirtcross"
|
||||
|
||||
/obj/item/clothing/suit/jacket/leather
|
||||
name = "leather jacket"
|
||||
desc = "Pompadour not included."
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var/hoodtype = /obj/item/clothing/head/hooded/winterhood //so the chaplain hoodie or other hoodies can override this
|
||||
///Alternative mode for hiding the hood, instead of storing the hood in the suit it qdels it, useful for when you deal with hooded suit with storage.
|
||||
var/alternative_mode = FALSE
|
||||
var/no_t //do not update sprites when pulling up hood so we can avoid oddities with certain mechanics
|
||||
|
||||
/obj/item/clothing/suit/hooded/Initialize()
|
||||
. = ..()
|
||||
@@ -51,6 +52,8 @@
|
||||
update_icon()
|
||||
|
||||
/obj/item/clothing/suit/hooded/update_icon_state()
|
||||
if(no_t)
|
||||
return
|
||||
icon_state = "[initial(icon_state)]"
|
||||
if(ishuman(hood?.loc))
|
||||
var/mob/living/carbon/human/H = hood.loc
|
||||
|
||||
@@ -18,3 +18,9 @@
|
||||
desc = "A slick black and red plasmaman containment suit designed for the head of security, also called the LAW."
|
||||
icon_state = "hos_envirosuit"
|
||||
item_state = "hos_envirosuit"
|
||||
|
||||
/obj/item/clothing/under/plasmaman/prisoner
|
||||
name = "prisoner envirosuit"
|
||||
desc = "An orange envirosuit identifying and protecting a criminal plasmaman."
|
||||
icon_state = "prisoner_envirosuit"
|
||||
item_state = "prisoner_envirosuit"
|
||||
|
||||
@@ -10,8 +10,28 @@
|
||||
|
||||
/datum/round_event/cat_surgeon/start()
|
||||
var/list/spawn_locs = list()
|
||||
var/list/unsafe_spawn_locs = list()
|
||||
for(var/X in GLOB.xeno_spawn)
|
||||
spawn_locs += X
|
||||
if(!isfloorturf(X))
|
||||
unsafe_spawn_locs += X
|
||||
continue
|
||||
var/turf/open/floor/F = X
|
||||
var/datum/gas_mixture/A = F.air
|
||||
var/oxy_moles = A.get_moles(GAS_O2)
|
||||
if((oxy_moles < 16 || oxy_moles > 50) || A.get_moles(GAS_PLASMA) || A.get_moles(GAS_CO2) >= 10)
|
||||
unsafe_spawn_locs += F
|
||||
continue
|
||||
if((A.return_temperature() <= 270) || (A.return_temperature() >= 360))
|
||||
unsafe_spawn_locs += F
|
||||
continue
|
||||
var/pressure = A.return_pressure()
|
||||
if((pressure <= 20) || (pressure >= 550))
|
||||
unsafe_spawn_locs += F
|
||||
continue
|
||||
spawn_locs += F
|
||||
|
||||
if(!spawn_locs.len)
|
||||
spawn_locs += unsafe_spawn_locs
|
||||
|
||||
if(!spawn_locs.len)
|
||||
message_admins("No valid spawn locations found, aborting...")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/datum/round_event_control/mass_hallucination/admin_setup()
|
||||
if(!check_rights(R_FUN))
|
||||
return
|
||||
|
||||
|
||||
forced_hallucination = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in subtypesof(/datum/hallucination)
|
||||
|
||||
/datum/round_event/mass_hallucination
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
switch(rand(1,4))
|
||||
if(1) //same sound for everyone
|
||||
var/sound = pick("airlock","airlock_pry","console","explosion","far_explosion","mech","glass","alarm","beepsky","mech","wall_decon","door_hack","tesla")
|
||||
var/sound = pick("airlock","airlock_pry","console","explosion","far_explosion","mech","glass","alarm","beepsky","mech","wall_decon","door_hack","tesla","seth")
|
||||
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
|
||||
new /datum/hallucination/sounds(C, TRUE, sound)
|
||||
if(2)
|
||||
|
||||
@@ -27,13 +27,27 @@
|
||||
if(prob(low_threat_perc))
|
||||
severity = "low; the supermatter should return to normal operation shortly."
|
||||
else
|
||||
severity = "medium; the supermatter should return to normal operation, but check NT CIMS to ensure this."
|
||||
severity = "medium; the supermatter should return to normal operation, but regardless, check if the emitters may need to be turned off temporarily."
|
||||
else
|
||||
severity = "high; if the supermatter's cooling is not fortified, coolant may need to be added."
|
||||
severity = "high; the emitters likely need to be turned off, and if the supermatter's cooling loop is not fortified, pre-cooled gas may need to be added."
|
||||
if(100000 to INFINITY)
|
||||
severity = "extreme; emergency action is likely to be required even if coolant loop is fine."
|
||||
severity = "extreme; emergency action is likely to be required even if coolant loop is fine. Turn off the emitters and make sure the loop is properly cooling gases."
|
||||
if(power > 20000 || prob(round(power/200)))
|
||||
priority_announce("Supermatter surge detected. Estimated severity is [severity]", "Anomaly Alert")
|
||||
|
||||
/datum/round_event/supermatter_surge/start()
|
||||
GLOB.main_supermatter_engine.matter_power += power
|
||||
var/obj/machinery/power/supermatter_crystal/supermatter = GLOB.main_supermatter_engine
|
||||
var/power_proportion = supermatter.powerloss_inhibitor/2 // what % of the power goes into matter power, at most 50%
|
||||
// we reduce the proportion that goes into actual matter power based on powerloss inhibitor
|
||||
// primarily so the supermatter doesn't tesla the instant these happen
|
||||
supermatter.matter_power += power * power_proportion
|
||||
var/datum/gas_mixture/methane_puff = new
|
||||
var/selected_gas = pick(4;GAS_CO2, 10;GAS_METHANE, 4;GAS_H2O, 1;GAS_BZ, 1;GAS_METHYL_BROMIDE)
|
||||
methane_puff.set_moles(selected_gas, 500)
|
||||
methane_puff.set_temperature(500)
|
||||
var/energy_ratio = (power * 500 * (1-power_proportion)) / methane_puff.thermal_energy()
|
||||
if(energy_ratio < 1) // energy output we want is lower than current energy, reduce the amount of gas we puff out
|
||||
methane_puff.set_moles(GAS_METHANE, energy_ratio * 500)
|
||||
else // energy output we want is higher than current energy, increase its actual heat
|
||||
methane_puff.set_temperature(energy_ratio * 500)
|
||||
supermatter.assume_air(methane_puff)
|
||||
|
||||
@@ -778,7 +778,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
..()
|
||||
var/turf/source = random_far_turf()
|
||||
if(!sound_type)
|
||||
sound_type = pick("airlock","airlock pry","console","flash","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack")
|
||||
sound_type = pick("airlock","airlock pry","console","flash","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack","seth")
|
||||
feedback_details += "Type: [sound_type]"
|
||||
//Strange audio
|
||||
switch(sound_type)
|
||||
@@ -827,6 +827,9 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
target.playsound_local(source, 'sound/items/screwdriver.ogg', 50, 1)
|
||||
sleep(rand(40,80))
|
||||
target.playsound_local(source, 'sound/machines/airlockforced.ogg', 30, 1)
|
||||
//funny announcement man
|
||||
if("seth")
|
||||
target.playsound_local(source,'sound/misc/seth.ogg', 50, 1)
|
||||
qdel(src)
|
||||
|
||||
/datum/hallucination/weird_sounds
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
custom_food_type = /obj/item/reagent_containers/food/snacks/customizable/sandwich
|
||||
filling_color = "#FFA500"
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 2)
|
||||
slot_flags = ITEM_SLOT_HEAD
|
||||
slot_flags = ITEM_SLOT_MASK
|
||||
customfoodfilling = 0 //to avoid infinite bread-ception
|
||||
foodtype = GRAIN
|
||||
dunkable = TRUE
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#define CONE_WAFFLE 8
|
||||
#define CONE_CHOC 9
|
||||
|
||||
|
||||
|
||||
/obj/machinery/icecream_vat
|
||||
name = "ice cream vat"
|
||||
desc = "Ding-aling ding dong. Get your Nanotrasen-approved ice cream!"
|
||||
@@ -35,6 +33,8 @@
|
||||
/datum/reagent/consumable/ethanol/singulo = 6,
|
||||
/datum/reagent/consumable/peachjuice = 6,
|
||||
/datum/reagent/consumable/grapejuice = 6)
|
||||
var/custom_taste
|
||||
var/custom_color
|
||||
|
||||
/obj/machinery/icecream_vat/proc/get_ingredient_list(type)
|
||||
switch(type)
|
||||
@@ -99,7 +99,10 @@
|
||||
dat += "<b>Peach ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_PEACH]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=5'><b>x5</b></a> [product_types[ICECREAM_PEACH]] dollops left. (Ingredients: milk, ice, peach juice)<br>"
|
||||
dat += "<b>Grape ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_GRAPE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=5'><b>x5</b></a> [product_types[ICECREAM_GRAPE]] dollops left. (Ingredients: milk, ice, grape juice)<br>"
|
||||
dat += "<b>Blue ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_BLUE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=5'><b>x5</b></a> [product_types[ICECREAM_BLUE]] dollops left. (Ingredients: milk, ice, singulo)<br>"
|
||||
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br></div>"
|
||||
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br>"
|
||||
dat += "<a href='?src=[REF(src)];custom_taste=1;'><b>Change custom taste: [custom_taste ? custom_taste : "Default"]</b></a>"
|
||||
dat += "<br><a href='?src=[REF(src)];custom_color=1;'><b>Change custom color: [custom_color ? custom_color : "Default"]</b></a>"
|
||||
dat += "<br><a href='?src=[REF(src)];reset_custom=1;'><b>Reset custom ice cream taste and color to defaults</b></a></div>"
|
||||
dat += "<br><b>CONES</b><br><div class='statusDisplay'>"
|
||||
dat += "<b>Waffle cones:</b> <a href='?src=[REF(src)];cone=[CONE_WAFFLE]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=5'><b>x5</b></a> [product_types[CONE_WAFFLE]] cones left. (Ingredients: flour, sugar)<br>"
|
||||
dat += "<b>Chocolate cones:</b> <a href='?src=[REF(src)];cone=[CONE_CHOC]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=5'><b>x5</b></a> [product_types[CONE_CHOC]] cones left. (Ingredients: flour, sugar, coco powder)<br></div>"
|
||||
@@ -128,7 +131,7 @@
|
||||
visible_message("[icon2html(src, viewers(src))] <span class='info'>[user] scoops delicious [flavour_name] ice cream into [I].</span>")
|
||||
product_types[dispense_flavour] -= 1
|
||||
if(beaker && beaker.reagents.total_volume)
|
||||
I.add_ice_cream(flavour_name, beaker.reagents)
|
||||
I.add_ice_cream(flavour_name, beaker.reagents, custom_color, custom_taste)
|
||||
else
|
||||
I.add_ice_cream(flavour_name)
|
||||
if(I.reagents.total_volume < 10)
|
||||
@@ -213,14 +216,25 @@
|
||||
if(href_list["refill"])
|
||||
RefillFromBeaker()
|
||||
|
||||
updateDialog()
|
||||
|
||||
if(href_list["refresh"])
|
||||
updateDialog()
|
||||
|
||||
if(href_list["close"])
|
||||
usr.unset_machine()
|
||||
usr << browse(null,"window=icecreamvat")
|
||||
|
||||
if(href_list["custom_taste"])
|
||||
custom_taste = stripped_input(usr, "Set a custom taste for the custom icecream. 50 characters max, leave blank to go back to the default option.", max_length = 50)
|
||||
|
||||
if(href_list["custom_color"])
|
||||
custom_color = input(usr, "Choose a color for the custom icecream. Cancel to go back to the default option.") as color|null
|
||||
|
||||
if(href_list["reset_custom"])
|
||||
custom_taste = null
|
||||
custom_color = null
|
||||
|
||||
updateDialog() // i have no clue why we even have refresh when this is a thing but sure
|
||||
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/icecream
|
||||
@@ -251,7 +265,7 @@
|
||||
desc = "Delicious [cone_name] cone, but no ice cream."
|
||||
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R)
|
||||
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R, custom_color, custom_taste)
|
||||
name = "[flavour_name] icecream"
|
||||
switch (flavour_name) // adding the actual reagents advertised in the ingredient list
|
||||
if ("vanilla")
|
||||
@@ -286,8 +300,11 @@
|
||||
if(R && R.total_volume >= 4) //consumable reagents have stronger taste so higher volume will allow non-food flavourings to break through better.
|
||||
var/mutable_appearance/flavoring = mutable_appearance(icon,"icecream_custom")
|
||||
var/datum/reagent/master = R.get_master_reagent()
|
||||
flavoring.color = master.color
|
||||
filling_color = master.color
|
||||
flavoring.color = custom_color ? custom_color : master.color
|
||||
filling_color = custom_color ? custom_color : master.color
|
||||
if(custom_taste)
|
||||
tastes = list("[custom_taste]" = 1)
|
||||
reagents.force_alt_taste = TRUE
|
||||
name = "[master.name] icecream"
|
||||
desc = "A delicious [cone_type] cone filled with artisanal icecream. Made with real [master.name]. Ain't that something."
|
||||
R.trans_to(src, 4)
|
||||
|
||||
@@ -641,6 +641,18 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
|
||||
/datum/holiday/easter/getStationPrefix()
|
||||
return pick("Fluffy","Bunny","Easter","Egg")
|
||||
|
||||
/datum/holiday/ianbirthday
|
||||
name = "Ian's Birthday" //github.com/tgstation/tgstation/commit/de7e4f0de0d568cd6e1f0d7bcc3fd34700598acb
|
||||
begin_month = SEPTEMBER
|
||||
begin_day = 9
|
||||
end_day = 10
|
||||
|
||||
/datum/holiday/ianbirthday/greet()
|
||||
return "Happy birthday, Ian!"
|
||||
|
||||
/datum/holiday/ianbirthday/getStationPrefix()
|
||||
return pick("Ian", "Corgi", "Erro")
|
||||
|
||||
//Random citadel thing for halloween species
|
||||
/proc/force_enable_halloween_species()
|
||||
var/list/oldlist = SSevents.holidays
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
/obj/item/reagent_containers/food/snacks/grown/carrot/attackby(obj/item/I, mob/user, params)
|
||||
if(I.get_sharpness())
|
||||
to_chat(user, "<span class='notice'>You sharpen the carrot into a shiv with [I].</span>")
|
||||
var/obj/item/kitchen/knife/carrotshiv/Shiv = new /obj/item/kitchen/knife/carrotshiv
|
||||
var/obj/item/kitchen/knife/shiv/carrot/Shiv = new /obj/item/kitchen/knife/shiv/carrot
|
||||
remove_item_from_storage(user)
|
||||
qdel(src)
|
||||
user.put_in_hands(Shiv)
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
var/turf/open/O = loc
|
||||
if(O.air)
|
||||
var/datum/gas_mixture/loc_air = O.air
|
||||
if(loc_air.get_moles(GAS_O2) > 13)
|
||||
if(loc_air.get_moles(GAS_O2) > 3)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
outputs = list(
|
||||
"registered name" = IC_PINTYPE_STRING,
|
||||
"assignment" = IC_PINTYPE_STRING,
|
||||
"passkey" = IC_PINTYPE_STRING
|
||||
)
|
||||
activators = list(
|
||||
"on read" = IC_PINTYPE_PULSE_OUT
|
||||
@@ -17,7 +16,6 @@
|
||||
/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent)
|
||||
var/obj/item/card/id/card = I.GetID()
|
||||
var/list/access = I.GetAccess()
|
||||
var/passkey = strtohex(XorEncrypt(json_encode(access), SScircuit.cipherkey))
|
||||
|
||||
if(assembly)
|
||||
assembly.access_card.access |= access
|
||||
@@ -33,8 +31,6 @@
|
||||
else
|
||||
return FALSE
|
||||
|
||||
set_pin_data(IC_OUTPUT, 3, passkey)
|
||||
|
||||
push_data()
|
||||
activate_pin(1)
|
||||
return TRUE
|
||||
|
||||
@@ -355,7 +355,7 @@
|
||||
return list("Assistant", "Captain", "Head of Personnel", "Bartender", "Cook", "Botanist", "Quartermaster", "Cargo Technician",
|
||||
"Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer",
|
||||
"Atmospheric Technician", "Chief Medical Officer", "Medical Doctor", "Chemist", "Geneticist", "Virologist", "Paramedic",
|
||||
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer")
|
||||
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer", "Prisoner")
|
||||
|
||||
/proc/get_all_job_icons() //For all existing HUD icons
|
||||
return get_all_jobs() + list("Prisoner")
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
chameleon_extras = /obj/item/stamp/ce
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
datum/job/prisoner
|
||||
title = "Prisoner"
|
||||
flag = PRISONER
|
||||
department_head = list("The Security Team")
|
||||
department_flag = CIVILIAN
|
||||
faction = "Station"
|
||||
total_positions = 0
|
||||
spawn_positions = 2
|
||||
supervisors = "the security team"
|
||||
|
||||
outfit = /datum/outfit/job/prisoner
|
||||
plasma_outfit = /datum/outfit/plasmaman/prisoner
|
||||
|
||||
display_order = JOB_DISPLAY_ORDER_PRISONER
|
||||
|
||||
/datum/outfit/job/prisoner
|
||||
name = "Prisoner"
|
||||
jobtype = /datum/job/prisoner
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/prisoner
|
||||
shoes = /obj/item/clothing/shoes/sneakers/orange
|
||||
id = /obj/item/card/id/prisoner
|
||||
ears = /obj/item/radio/headset/headset_prisoner
|
||||
belt = null
|
||||
@@ -133,7 +133,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
backpack = /obj/item/storage/backpack/explorer
|
||||
satchel = /obj/item/storage/backpack/satchel/explorer
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag
|
||||
box = /obj/item/storage/box/survival_mining
|
||||
box = /obj/item/storage/box/survival/mining
|
||||
|
||||
chameleon_extras = /obj/item/gun/energy/kinetic_accelerator
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ GLOBAL_LIST_INIT(civilian_positions, list(
|
||||
"Chaplain",
|
||||
"Clown",
|
||||
"Mime",
|
||||
"Prisoner",
|
||||
"Assistant"))
|
||||
|
||||
GLOBAL_LIST_INIT(security_positions, list(
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
#define MAFIA_TEAM_SOLO "solo"
|
||||
|
||||
//types of town roles for random setup gen
|
||||
/// assistants it's just assistants filling up the rest of the roles
|
||||
/// Add this if you don't want a role to be a choice in the selection
|
||||
#define TOWN_OVERFLOW "overflow"
|
||||
/// roles that learn info about others in the game (chaplain, detective, psych)
|
||||
#define TOWN_INVEST "invest"
|
||||
/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts)
|
||||
/// roles that keep other roles safe (doctor, sec officer, and weirdly enough lawyer counts)
|
||||
#define TOWN_PROTECT "protect"
|
||||
/// roles that are only there to kill bad guys.
|
||||
#define TOWN_KILLING "killing"
|
||||
/// roles that don't fit into anything else (hop)
|
||||
#define TOWN_MISC "misc"
|
||||
#define TOWN_SUPPORT "support"
|
||||
|
||||
//other types (mafia team, neutrals)
|
||||
/// normal vote kill changelings
|
||||
@@ -25,6 +27,20 @@
|
||||
/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia
|
||||
#define NEUTRAL_DISRUPT "disrupt"
|
||||
|
||||
//role flags (special status of roles like detection immune)
|
||||
///to all forms of detection, shows themselves as an assistant.
|
||||
#define ROLE_UNDETECTABLE (1<<0)
|
||||
///has the ability to kill at night and thus, blocks the game from ending with other teams alive.
|
||||
#define ROLE_CAN_KILL (1<<1)
|
||||
///can only be one in a randomly generated game
|
||||
#define ROLE_UNIQUE (1<<2)
|
||||
///role is public to all other players in the game.
|
||||
#define ROLE_REVEALED (1<<3)
|
||||
///can not be defended, protected, or any other form of protection. all kills succeed no matter what.
|
||||
#define ROLE_VULNERABLE (1<<4)
|
||||
///cannot perform any actions that night, preselected actions fail
|
||||
#define ROLE_ROLEBLOCKED (1<<5)
|
||||
|
||||
#define MAFIA_PHASE_SETUP 1
|
||||
#define MAFIA_PHASE_DAY 2
|
||||
#define MAFIA_PHASE_VOTING 3
|
||||
@@ -35,12 +51,12 @@
|
||||
#define MAFIA_ALIVE 1
|
||||
#define MAFIA_DEAD 2
|
||||
|
||||
#define COMSIG_MAFIA_ON_VISIT "mafia_onvisit"
|
||||
#define MAFIA_VISIT_INTERRUPTED 1
|
||||
|
||||
#define COMSIG_MAFIA_ON_KILL "mafia_onkill"
|
||||
#define MAFIA_PREVENT_KILL 1
|
||||
|
||||
#define COMSIG_MAFIA_CAN_PERFORM_ACTION "mafia_can_perform_action"
|
||||
#define MAFIA_PREVENT_ACTION 1
|
||||
|
||||
//in order of events + game end
|
||||
|
||||
/// when the shutters fall, before the 45 second wait and night event resolution
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
var/phase = MAFIA_PHASE_SETUP
|
||||
///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc.
|
||||
var/turn = 0
|
||||
///if enabled, the game has fallen under half pop and is sped up
|
||||
var/speed_up = FALSE
|
||||
///for debugging and testing a full game, or adminbuse. If this is not empty, it will use this as a setup. clears when game is over
|
||||
var/list/custom_setup = list()
|
||||
///first day has no voting, and thus is shorter
|
||||
@@ -187,8 +189,22 @@
|
||||
turn += 1
|
||||
phase = MAFIA_PHASE_DAY
|
||||
if(!check_victory())
|
||||
if(!speed_up)//lets check if the game should be sped up, if not already.
|
||||
var/living_players = 0
|
||||
for(var/i in all_roles)
|
||||
var/datum/mafia_role/player = i
|
||||
if(player.game_status == MAFIA_ALIVE)
|
||||
living_players += 1
|
||||
if(living_players < all_roles.len / 2)
|
||||
speed_up = TRUE
|
||||
send_message("<span class='bold notice'>With only [living_players] living players left, the game timers have been sped up.</span>")
|
||||
day_phase_period /= 2
|
||||
voting_phase_period /= 2
|
||||
judgement_phase_period /= 2
|
||||
judgement_lynch_period /= 2
|
||||
night_phase_period /= 2
|
||||
if(turn == 1)
|
||||
send_message("<span class='notice'><b>The selected map is [current_map.name]!</b></br>[current_map.description]</span>")
|
||||
send_message(span_notice("<b>The selected map is [current_map.name]!</b></br>[current_map.description]"))
|
||||
send_message("<b>Day [turn] started! There is no voting on the first day. Say hello to everybody!</b>")
|
||||
next_phase_timer = addtimer(CALLBACK(src,.proc/check_trial, FALSE),first_day_phase_period,TIMER_STOPPABLE) //no voting period = no votes = instant night
|
||||
else
|
||||
@@ -257,19 +273,19 @@
|
||||
/datum/mafia_controller/proc/lynch()
|
||||
for(var/i in judgement_innocent_votes)
|
||||
var/datum/mafia_role/role = i
|
||||
send_message("<span class='green'>[role.body.real_name] voted innocent.</span>")
|
||||
send_message(span_green("[role.body.real_name] voted innocent."))
|
||||
for(var/ii in judgement_abstain_votes)
|
||||
var/datum/mafia_role/role = ii
|
||||
send_message("<span class='comradio'>[role.body.real_name] abstained.</span>")
|
||||
send_message(span_comradio("[role.body.real_name] abstained."))
|
||||
for(var/iii in judgement_guilty_votes)
|
||||
var/datum/mafia_role/role = iii
|
||||
send_message("<span class='red'>[role.body.real_name] voted guilty.</span>")
|
||||
send_message(span_red("[role.body.real_name] voted guilty."))
|
||||
if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch
|
||||
send_message("<span class='red'><b>Guilty wins majority, [on_trial.body.real_name] has been lynched.</b></span>")
|
||||
on_trial.kill(src, lynch = TRUE)
|
||||
send_message(span_red("<b>Guilty wins majority, [on_trial.body.real_name] has been lynched.</b>"))
|
||||
on_trial.kill(src,lynch = TRUE)
|
||||
addtimer(CALLBACK(src, .proc/send_home, on_trial),judgement_lynch_period)
|
||||
else
|
||||
send_message("<span class='green'><b>Innocent wins majority, [on_trial.body.real_name] has been spared.</b></span>")
|
||||
send_message(span_green("<b>Innocent wins majority, [on_trial.body.real_name] has been spared.</b>"))
|
||||
on_trial.body.forceMove(get_turf(on_trial.assigned_landmark))
|
||||
on_trial = null
|
||||
//day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else
|
||||
@@ -300,11 +316,14 @@
|
||||
var/list/total_town = list()
|
||||
var/list/total_mafia = list()
|
||||
|
||||
var/alive_town = 0
|
||||
//voting power of town + solos (since they don't want mafia to overpower)
|
||||
var/anti_mafia_power = 0
|
||||
//voting power of mafia (greater than anti mafia power + team end not blocked = mafia victory)
|
||||
var/alive_mafia = 0
|
||||
var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes
|
||||
var/list/total_victors = list() //if this list gets filled with anyone, they win. list because side antags can with with people
|
||||
var/blocked_victory = FALSE //if a solo antagonist is stopping the town or mafia from finishing the game.
|
||||
var/town_can_kill = FALSE //Town has a killing role and it cannot allow mafia to win
|
||||
|
||||
///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE
|
||||
|
||||
@@ -313,23 +332,24 @@
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
total_mafia += R
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
alive_mafia += R.vote_power
|
||||
alive_mafia += R.vote_potential
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
total_town += R
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
alive_town += R.vote_power
|
||||
anti_mafia_power += R.vote_potential
|
||||
if(R.role_flags & ROLE_CAN_KILL) //the game cannot autoresolve with killing roles (unless a solo wins anyways, like traitors who are immune)
|
||||
town_can_kill = TRUE
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
if(R.solo_counts_as_town)
|
||||
alive_town += R.vote_power
|
||||
anti_mafia_power += R.vote_potential
|
||||
solos_to_ask += R
|
||||
|
||||
///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN
|
||||
|
||||
for(var/datum/mafia_role/solo in solos_to_ask)
|
||||
if(solo.check_total_victory(alive_town, alive_mafia))
|
||||
if(solo.check_total_victory(anti_mafia_power, alive_mafia))
|
||||
total_victors += solo
|
||||
if(solo.block_team_victory(alive_town, alive_mafia))
|
||||
if(solo.block_team_victory(anti_mafia_power, alive_mafia))
|
||||
blocked_victory = TRUE
|
||||
|
||||
//solo victories!
|
||||
@@ -348,7 +368,7 @@
|
||||
award_role(townie.winner_award, townie)
|
||||
start_the_end("<span class='big green'>!! TOWN VICTORY !!</span>")
|
||||
return TRUE
|
||||
else if(alive_mafia >= alive_town) //guess could change if town nightkill is added
|
||||
else if(alive_mafia >= anti_mafia_power && !town_can_kill)
|
||||
start_the_end("<span class='big red'>!! MAFIA VICTORY !!</span>")
|
||||
for(var/datum/mafia_role/changeling in total_mafia)
|
||||
award_role(changeling.winner_award, changeling)
|
||||
@@ -396,6 +416,13 @@
|
||||
custom_setup = list()
|
||||
turn = 0
|
||||
votes = list()
|
||||
|
||||
day_phase_period = initial(day_phase_period)
|
||||
voting_phase_period = initial(voting_phase_period)
|
||||
judgement_phase_period = initial(judgement_phase_period)
|
||||
judgement_lynch_period = initial(judgement_lynch_period)
|
||||
night_phase_period = initial(night_phase_period)
|
||||
|
||||
//map gen does not deal with landmarks
|
||||
QDEL_LIST(landmarks)
|
||||
QDEL_NULL(town_center_landmark)
|
||||
@@ -451,14 +478,15 @@
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START)
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE)
|
||||
//resolve mafia kill, todo unsnowflake this
|
||||
var/datum/mafia_role/R = get_vote_winner("Mafia")
|
||||
if(R)
|
||||
var/datum/mafia_role/victim = get_vote_winner("Mafia")
|
||||
if(victim)
|
||||
var/datum/mafia_role/killer = get_random_voter("Mafia")
|
||||
if(SEND_SIGNAL(killer,COMSIG_MAFIA_CAN_PERFORM_ACTION,src,"mafia killing",R) & MAFIA_PREVENT_ACTION)
|
||||
send_message("<span class='danger'>[killer.body.real_name] was unable to attack [R.body.real_name] tonight!</span>",MAFIA_TEAM_MAFIA)
|
||||
if(!victim.can_action(src, killer, "changeling murder"))
|
||||
send_message(span_danger("[killer.body.real_name] was unable to attack [victim.body.real_name] tonight!"),MAFIA_TEAM_MAFIA)
|
||||
else
|
||||
send_message("<span class='danger'>[killer.body.real_name] has attacked [R.body.real_name]!</span>",MAFIA_TEAM_MAFIA)
|
||||
R.kill(src)
|
||||
send_message(span_danger("[killer.body.real_name] has attacked [victim.body.real_name]!"),MAFIA_TEAM_MAFIA)
|
||||
if(victim.kill(src,killer,lynch=FALSE))
|
||||
to_chat(victim.body, span_userdanger("You have been killed by a Changeling!"))
|
||||
reset_votes("Mafia")
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_KILL_PHASE)
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_END)
|
||||
@@ -485,14 +513,14 @@
|
||||
else
|
||||
votes[vote_type][voter] = target
|
||||
if(old_vote && old_vote == target)
|
||||
send_message("<span class='notice'>[voter.body.real_name] retracts their vote for [target.body.real_name]!</span>", team = teams)
|
||||
send_message(span_notice("[voter.body.real_name] retracts their vote for [target.body.real_name]!"), team = teams)
|
||||
else
|
||||
send_message("<span class='notice'>[voter.body.real_name] voted for [target.body.real_name]!</span>",team = teams)
|
||||
send_message(span_notice("[voter.body.real_name] voted for [target.body.real_name]!"),team = teams)
|
||||
if(!teams)
|
||||
target.body.update_icon() //Update the vote display if it's a public vote
|
||||
target.body.update_appearance() //Update the vote display if it's a public vote
|
||||
var/datum/mafia_role/old = old_vote
|
||||
if(old)
|
||||
old.body.update_icon()
|
||||
old.body.update_appearance()
|
||||
|
||||
/**
|
||||
* Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched
|
||||
@@ -504,7 +532,7 @@
|
||||
bodies_to_update += R.body
|
||||
votes[vote_type] = list()
|
||||
for(var/mob/M in bodies_to_update)
|
||||
M.update_icon()
|
||||
M.update_appearance()
|
||||
|
||||
/**
|
||||
* Returns how many people voted for the role, in whatever vote (day vote, night kill vote)
|
||||
@@ -571,6 +599,9 @@
|
||||
/datum/mafia_controller/proc/create_bodies()
|
||||
for(var/datum/mafia_role/role in all_roles)
|
||||
var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
|
||||
ADD_TRAIT(H, TRAIT_NOFIRE, MAFIA_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_NOBREATH, MAFIA_TRAIT)
|
||||
// ADD_TRAIT(H, TRAIT_CANNOT_CRYSTALIZE, MAFIA_TRAIT) freon tomfoolery
|
||||
H.equipOutfit(player_outfit)
|
||||
H.status_flags |= GODMODE
|
||||
RegisterSignal(H,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/display_votes)
|
||||
@@ -707,6 +738,7 @@
|
||||
if(role_count > 0)
|
||||
debug_setup[found_path] = role_count
|
||||
custom_setup = debug_setup
|
||||
try_autostart()//don't worry, this fails if there's a game in progress
|
||||
if("cancel_setup")
|
||||
custom_setup = list()
|
||||
switch(action) //both living and dead
|
||||
@@ -723,28 +755,28 @@
|
||||
switch(action)
|
||||
if("mf_signup")
|
||||
if(!SSticker.HasRoundStarted())
|
||||
to_chat(usr, "<span class='warning'>Wait for the round to start.</span>")
|
||||
to_chat(usr, span_warning("Wait for the round to start."))
|
||||
return
|
||||
if(GLOB.mafia_signup[C.ckey])
|
||||
GLOB.mafia_signup -= C.ckey
|
||||
to_chat(usr, "<span class='notice'>You unregister from Mafia.</span>")
|
||||
to_chat(usr, span_notice("You unregister from Mafia."))
|
||||
return TRUE
|
||||
else
|
||||
GLOB.mafia_signup[C.ckey] = C
|
||||
to_chat(usr, "<span class='notice'>You sign up for Mafia.</span>")
|
||||
to_chat(usr, span_notice("You sign up for Mafia."))
|
||||
if(phase == MAFIA_PHASE_SETUP)
|
||||
check_signups()
|
||||
try_autostart()
|
||||
return TRUE
|
||||
if("mf_spectate")
|
||||
if(C.ckey in spectators)
|
||||
to_chat(usr, "<span class='notice'>You will no longer get messages from the game.</span>")
|
||||
to_chat(usr, span_notice("You will no longer get messages from the game."))
|
||||
spectators -= C.ckey
|
||||
else
|
||||
to_chat(usr, "<span class='notice'>You will now get messages from the game.</span>")
|
||||
to_chat(usr, span_notice("You will now get messages from the game."))
|
||||
spectators += C.ckey
|
||||
return TRUE
|
||||
if(user_role.game_status == MAFIA_DEAD)
|
||||
if(user_role && user_role.game_status == MAFIA_DEAD)
|
||||
return
|
||||
//User actions (just living)
|
||||
switch(action)
|
||||
@@ -814,58 +846,59 @@
|
||||
. += L[key]
|
||||
|
||||
/**
|
||||
* Returns a semirandom setup, with...
|
||||
* Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town.
|
||||
* Mafia, 2 normal mafia and one special.
|
||||
* Neutral, two disruption roles, sometimes one is a killing.
|
||||
* Returns a semirandom setup with 12 roles. balance not guaranteed!
|
||||
*
|
||||
* See _defines.dm in the mafia folder for a rundown on what these groups of roles include.
|
||||
* please check the variables at the top of the proc to see how much of each role types it picks
|
||||
*/
|
||||
/datum/mafia_controller/proc/generate_random_setup()
|
||||
var/invests_left = 2
|
||||
var/protects_left = 1
|
||||
var/miscs_left = prob(35)
|
||||
var/protects_left = 2
|
||||
var/killings_left = 1
|
||||
var/supports_left = 2
|
||||
|
||||
var/mafiareg_left = 2
|
||||
var/mafiaspe_left = 1
|
||||
var/killing_role = prob(50)
|
||||
var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow
|
||||
var/overflow_left = max_player - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors)
|
||||
|
||||
// if there is one killing role, there will be less disruptors
|
||||
var/neutral_killing_role = prob(50)
|
||||
|
||||
var/list/random_setup = list()
|
||||
var/list/unique_roles_added = list()
|
||||
for(var/i in 1 to max_player) //should match the number of roles to add
|
||||
if(overflow_left)
|
||||
add_setup_role(random_setup, TOWN_OVERFLOW)
|
||||
overflow_left--
|
||||
else if(invests_left)
|
||||
add_setup_role(random_setup, TOWN_INVEST)
|
||||
if(invests_left)
|
||||
add_setup_role(random_setup, unique_roles_added, TOWN_INVEST)
|
||||
invests_left--
|
||||
else if(protects_left)
|
||||
add_setup_role(random_setup, TOWN_PROTECT)
|
||||
add_setup_role(random_setup, unique_roles_added, TOWN_PROTECT)
|
||||
protects_left--
|
||||
else if(miscs_left)
|
||||
add_setup_role(random_setup, TOWN_MISC)
|
||||
miscs_left--
|
||||
else if(killings_left)
|
||||
add_setup_role(random_setup, unique_roles_added, TOWN_KILLING)
|
||||
killings_left--
|
||||
else if(supports_left)
|
||||
add_setup_role(random_setup, unique_roles_added, TOWN_SUPPORT)
|
||||
supports_left--
|
||||
else if(mafiareg_left)
|
||||
add_setup_role(random_setup, MAFIA_REGULAR)
|
||||
add_setup_role(random_setup, unique_roles_added, MAFIA_REGULAR)
|
||||
mafiareg_left--
|
||||
else if(mafiaspe_left)
|
||||
add_setup_role(random_setup, MAFIA_SPECIAL)
|
||||
add_setup_role(random_setup, unique_roles_added, MAFIA_SPECIAL)
|
||||
mafiaspe_left--
|
||||
else if(killing_role)
|
||||
add_setup_role(random_setup, NEUTRAL_KILL)
|
||||
killing_role--
|
||||
else if(neutral_killing_role)
|
||||
add_setup_role(random_setup, unique_roles_added, NEUTRAL_KILL)
|
||||
neutral_killing_role--
|
||||
else
|
||||
add_setup_role(random_setup, NEUTRAL_DISRUPT)
|
||||
add_setup_role(random_setup, unique_roles_added, NEUTRAL_DISRUPT)
|
||||
debug = random_setup
|
||||
return random_setup
|
||||
|
||||
/**
|
||||
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one
|
||||
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one. unique roles can only get added once.
|
||||
*/
|
||||
/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type)
|
||||
/datum/mafia_controller/proc/add_setup_role(setup_list, banned_roles, wanted_role_type)
|
||||
var/list/role_type_paths = list()
|
||||
for(var/path in typesof(/datum/mafia_role))
|
||||
var/datum/mafia_role/instance = path
|
||||
if(initial(instance.role_type) == wanted_role_type)
|
||||
if(initial(instance.role_type) == wanted_role_type && !(path in banned_roles))
|
||||
role_type_paths += instance
|
||||
|
||||
var/mafia_path = pick(role_type_paths)
|
||||
@@ -880,6 +913,8 @@
|
||||
setup_list[found_role] += 1
|
||||
return
|
||||
setup_list[mafia_path] = 1
|
||||
if(initial(mafia_path_type.role_flags) & ROLE_UNIQUE) //check to see if we should no longer consider this okay to add to the game
|
||||
banned_roles += mafia_path
|
||||
|
||||
/**
|
||||
* Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START.
|
||||
@@ -893,6 +928,8 @@
|
||||
var/list/setup = custom_setup
|
||||
if(!setup.len)
|
||||
req_players = max_player //MAFIA_MAX_PLAYER_COUNT
|
||||
else if(low_pop_mode)
|
||||
req_players = required_player
|
||||
else
|
||||
req_players = assoc_value_sum(setup)
|
||||
|
||||
@@ -909,10 +946,10 @@
|
||||
GLOB.mafia_signup -= key //not valid to play when we checked so remove them from signups
|
||||
|
||||
//if there were not enough players, don't start. we already trimmed the list to now hold only valid signups
|
||||
|
||||
if(length(possible_keys) < req_players)
|
||||
return
|
||||
else //hacky implementation of max players
|
||||
req_players = clamp(length(possible_keys), 1, max_player)
|
||||
req_players = clamp(length(possible_keys), required_player, max_player)
|
||||
|
||||
//if there were too many players, still start but only make filtered keys as big as it needs to be (cut excess)
|
||||
//also removes people who do get into final player list from the signup so they have to sign up again when game ends
|
||||
@@ -923,8 +960,8 @@
|
||||
//small message about not getting into this game for clarity on why they didn't get in
|
||||
for(var/unpicked in possible_keys)
|
||||
var/client/unpicked_client = GLOB.directory[unpicked]
|
||||
to_chat(unpicked_client, "<span class='danger'>Sorry, the starting mafia game has too many players and you were not picked.</span>")
|
||||
to_chat(unpicked_client, "<span class='warning'>You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.</span>")
|
||||
to_chat(unpicked_client, span_danger("Sorry, the starting mafia game has too many players and you were not picked."))
|
||||
to_chat(unpicked_client, span_warning("You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes."))
|
||||
|
||||
if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start.
|
||||
setup = generate_random_setup()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
requires_power = FALSE
|
||||
has_gravity = STANDARD_GRAVITY
|
||||
flags_1 = NONE
|
||||
// block_suicide = TRUE
|
||||
area_flags = BLOCK_SUICIDE | UNIQUE_AREA
|
||||
|
||||
/datum/map_template/mafia
|
||||
var/description = ""
|
||||
@@ -75,5 +75,5 @@
|
||||
|
||||
/datum/map_template/mafia/reebe
|
||||
name = "Reebe"
|
||||
description = "Trouble in Reebe station! Copypaste guranteed by ClockCo™"
|
||||
description = "Based of the place known as <i>reebee</i>. Syndicate spies have infiltrated and everyone has to find out who's who before they destroy the ark!"
|
||||
mappath = "_maps/map_files/Mafia/mafia_reebe.dmm"
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
uniform = /obj/item/clothing/under/suit/black
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
|
||||
/datum/outfit/mafia/chaplain
|
||||
name = "Mafia Chaplain"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/chaplain
|
||||
|
||||
/datum/outfit/mafia/md
|
||||
name = "Mafia Medical Doctor"
|
||||
|
||||
@@ -37,10 +42,14 @@
|
||||
shoes = /obj/item/clothing/shoes/sneakers/white
|
||||
suit = /obj/item/clothing/suit/toggle/labcoat
|
||||
|
||||
/datum/outfit/mafia/chaplain
|
||||
name = "Mafia Chaplain"
|
||||
/datum/outfit/mafia/security
|
||||
name = "Mafia Security Officer"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/chaplain
|
||||
uniform = /obj/item/clothing/under/rank/security/officer
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
head = /obj/item/clothing/head/helmet/sec
|
||||
suit = /obj/item/clothing/suit/armor/vest/alt
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
|
||||
/datum/outfit/mafia/lawyer
|
||||
name = "Mafia Lawyer"
|
||||
@@ -58,6 +67,26 @@
|
||||
head = /obj/item/clothing/head/hopcap
|
||||
glasses = /obj/item/clothing/glasses/sunglasses
|
||||
|
||||
/datum/outfit/mafia/hos
|
||||
name = "Mafia Head of Security"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/security/head_of_security
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
suit = /obj/item/clothing/suit/armor/hos/trenchcoat
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
head = /obj/item/clothing/head/beret/sec
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
|
||||
/datum/outfit/mafia/warden
|
||||
name = "Mafia Warden"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/security/warden
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
suit = /obj/item/clothing/suit/armor/vest/warden/alt
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
head = /obj/item/clothing/head/warden
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
|
||||
//mafia
|
||||
|
||||
/datum/outfit/mafia/changeling
|
||||
|
||||
+385
-154
@@ -10,19 +10,21 @@
|
||||
var/mob/living/carbon/human/body
|
||||
var/obj/effect/landmark/mafia/assigned_landmark
|
||||
|
||||
///how many votes submitted when you vote.
|
||||
///role flags (special status of roles like detection immune)
|
||||
var/role_flags = NONE
|
||||
///how many votes submitted when you vote. used in voting, but not victory
|
||||
var/vote_power = 1
|
||||
var/detect_immune = FALSE
|
||||
var/revealed = FALSE
|
||||
var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment
|
||||
//action = uses
|
||||
///how many votes your role COULD count for, now or later. used in checking victory
|
||||
var/vote_potential = 1
|
||||
///what they get equipped with when they are revealed
|
||||
var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant
|
||||
///action = uses
|
||||
var/list/actions = list()
|
||||
var/list/targeted_actions = list()
|
||||
//what the role gets when it wins a game
|
||||
///what the role gets when it wins a game
|
||||
var/winner_award = /datum/award/achievement/mafia/assistant
|
||||
|
||||
//so mafia have to also kill them to have a majority
|
||||
var/solo_counts_as_town = FALSE //(don't set this for town)
|
||||
///so mafia have to also kill them to have a majority
|
||||
var/game_status = MAFIA_ALIVE
|
||||
|
||||
///icon state in the mafia dmi of the hud of the role, used in the mafia ui
|
||||
@@ -38,8 +40,30 @@
|
||||
/datum/mafia_role/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
|
||||
/datum/mafia_role/proc/kill(datum/mafia_controller/game,lynch=FALSE)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,lynch) & MAFIA_PREVENT_KILL)
|
||||
/**
|
||||
* Tests if a visitor can actually perform an action on this role. Verbose on purpose!
|
||||
*
|
||||
* Will return false if: Your visit is roleblocked, they have perished, or your visit was interrupted
|
||||
*/
|
||||
/datum/mafia_role/proc/can_action(datum/mafia_controller/game, datum/mafia_role/visitor, action)
|
||||
if(role_flags & ROLE_ROLEBLOCKED)
|
||||
to_chat(visitor,span_danger("Your [action] was blocked!"))
|
||||
return FALSE
|
||||
if(game_status != MAFIA_ALIVE) //They're already dead
|
||||
to_chat(visitor,span_danger("[body.real_name] perished before you could visit!"))
|
||||
return FALSE
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_VISIT,game,visitor) & MAFIA_VISIT_INTERRUPTED) //visited a warden. something that prevents you by visiting that person
|
||||
to_chat(visitor,span_danger("Your [action] was interrupted!"))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Tests kill immunities, if nothing prevents the kill, kills this role.
|
||||
*
|
||||
* Does not count as visiting, see visit proc.
|
||||
*/
|
||||
/datum/mafia_role/proc/kill(datum/mafia_controller/game, datum/mafia_role/attacker, lynch=FALSE)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,attacker,lynch) & MAFIA_PREVENT_KILL)
|
||||
return FALSE
|
||||
game_status = MAFIA_DEAD
|
||||
body.death()
|
||||
@@ -55,19 +79,19 @@
|
||||
|
||||
/datum/mafia_role/proc/greet()
|
||||
SEND_SOUND(body, 'sound/ambience/ambifailure.ogg')
|
||||
to_chat(body,"<span class='danger'>You are the [name].</span>")
|
||||
to_chat(body,"<span class='danger'>[desc]</span>")
|
||||
to_chat(body,span_danger("You are the [name]."))
|
||||
to_chat(body,span_danger("[desc]"))
|
||||
switch(team)
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
to_chat(body,"<span class='danger'>You and your co-conspirators win if you outnumber crewmembers.</span>")
|
||||
to_chat(body,span_danger("You and your co-conspirators win if you outnumber crewmembers."))
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
to_chat(body,"<span class='danger'>You are a crewmember. Find out and lynch the changelings!</span>")
|
||||
to_chat(body,span_danger("You are a crewmember. Find out and lynch the changelings!"))
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
to_chat(body,"<span class='danger'>You are not aligned to town or mafia. Accomplish your own objectives!</span>")
|
||||
to_chat(body, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Mafia\">the wiki page</a> to learn more, if you have no idea what's going on.</b>")
|
||||
to_chat(body,span_danger("You are not aligned to town or mafia. Accomplish your own objectives!"))
|
||||
to_chat(body, "<span class='warningplain'><b>Be sure to read <a href=\"https://tgstation13.org/wiki/Mafia\">the wiki page</a> to learn more, if you have no idea what's going on.</b></span>")
|
||||
|
||||
/datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE)
|
||||
if(revealed)
|
||||
if((role_flags & ROLE_REVEALED))
|
||||
return
|
||||
if(verbose)
|
||||
game.send_message("<span class='big bold notice'>It is revealed that the true role of [body] [game_status == MAFIA_ALIVE ? "is" : "was"] [name]!</span>")
|
||||
@@ -76,7 +100,7 @@
|
||||
qdel(thing)
|
||||
special_reveal_equip(game)
|
||||
body.equipOutfit(revealed_outfit)
|
||||
revealed = TRUE
|
||||
role_flags |= ROLE_REVEALED
|
||||
|
||||
/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game)
|
||||
return
|
||||
@@ -85,7 +109,7 @@
|
||||
return
|
||||
|
||||
/datum/mafia_role/proc/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,action,target) & MAFIA_PREVENT_ACTION)
|
||||
if((role_flags & ROLE_ROLEBLOCKED))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -114,9 +138,9 @@
|
||||
team_desc = "Nobody"
|
||||
team_span = "comradio"
|
||||
the = FALSE
|
||||
result += "<span class='notice'>The <span class='bold'>[name]</span> is aligned with [the ? "the " : ""]<span class='[team_span]'>[team_desc]</span></span>"
|
||||
result += span_notice("The [span_bold("[name]")] is aligned with [the ? "the " : ""]<span class='[team_span]'>[team_desc]</span>")
|
||||
result += "<span class='bold notice'>\"[desc]\"</span>"
|
||||
result += "<span class='notice'>[name] wins when they [win_condition]</span>"
|
||||
result += span_notice("[name] wins when they [win_condition]")
|
||||
to_chat(clueless, result.Join("</br>"))
|
||||
|
||||
/datum/mafia_role/detective
|
||||
@@ -145,35 +169,39 @@
|
||||
|
||||
/datum/mafia_role/detective/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(!target || target.game_status != MAFIA_ALIVE)
|
||||
to_chat(body,"<span class='warning'>You can only investigate alive people.</span>")
|
||||
to_chat(body,span_warning("You can only investigate alive people."))
|
||||
return
|
||||
to_chat(body,"<span class='warning'>You will investigate [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will investigate [target.body.real_name] tonight."))
|
||||
current_investigation = target
|
||||
|
||||
/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!current_investigation)
|
||||
return
|
||||
|
||||
var/datum/mafia_role/target = current_investigation
|
||||
if(target)
|
||||
if(target.detect_immune)
|
||||
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is a true member of the station.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Town")
|
||||
else
|
||||
var/team_text
|
||||
var/fluff
|
||||
switch(target.team)
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
team_text = "Town"
|
||||
fluff = "a true member of the station."
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
team_text = "Mafia"
|
||||
fluff = "an unfeeling, hideous changeling!"
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
team_text = "Solo"
|
||||
fluff = "a rogue, with their own objectives..."
|
||||
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is [fluff]</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - [team_text]")
|
||||
current_investigation = null
|
||||
if(!target.can_action(game, src, "investigation"))
|
||||
return
|
||||
if((target.role_flags & ROLE_UNDETECTABLE))
|
||||
to_chat(body,span_warning("Your investigations reveal that [target.body.real_name] is a true member of the station."))
|
||||
add_note("N[game.turn] - [target.body.real_name] - Town")
|
||||
else
|
||||
var/team_text
|
||||
var/fluff
|
||||
switch(target.team)
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
team_text = "Town"
|
||||
fluff = "a true member of the station."
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
team_text = "Mafia"
|
||||
fluff = "an unfeeling, hideous changeling!"
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
team_text = "Solo"
|
||||
fluff = "a rogue, with their own objectives..."
|
||||
to_chat(body,span_warning("Your investigations reveal that [target.body.real_name] is [fluff]"))
|
||||
add_note("N[game.turn] - [target.body.real_name] - [team_text]")
|
||||
|
||||
/datum/mafia_role/psychologist
|
||||
name = "Psychologist"
|
||||
@@ -195,25 +223,27 @@
|
||||
|
||||
/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src)
|
||||
if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || (target.role_flags & ROLE_REVEALED) || target == src)
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
to_chat(body,"<span class='warning'>You will reveal [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will reveal [target.body.real_name] tonight."))
|
||||
current_target = target
|
||||
|
||||
/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer.
|
||||
current_target = null
|
||||
if(current_target)
|
||||
add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity")
|
||||
to_chat(body,"<span class='warning'>You have revealed the true nature of the [current_target]!</span>")
|
||||
current_target.reveal_role(game, verbose = TRUE)
|
||||
current_target = null
|
||||
can_use = FALSE
|
||||
if(!current_target)
|
||||
return
|
||||
var/datum/mafia_role/target = current_target
|
||||
current_target = null
|
||||
if(!target.can_action(game, src, "role reveal"))
|
||||
return
|
||||
add_note("N[game.turn] - [target.body.real_name] - Revealed true identity")
|
||||
to_chat(body,span_warning("You have revealed the true nature of the [target]!"))
|
||||
target.reveal_role(game, verbose = TRUE)
|
||||
can_use = FALSE
|
||||
|
||||
/datum/mafia_role/chaplain
|
||||
name = "Chaplain"
|
||||
@@ -225,7 +255,7 @@
|
||||
winner_award = /datum/award/achievement/mafia/chaplain
|
||||
|
||||
targeted_actions = list("Pray")
|
||||
var/current_target
|
||||
var/datum/mafia_role/current_target
|
||||
|
||||
/datum/mafia_role/chaplain/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
@@ -235,25 +265,29 @@
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !(target.role_flags & ROLE_REVEALED)
|
||||
|
||||
/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
to_chat(body,"<span class='warning'>You will commune with the spirit of [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will commune with the spirit of [target.body.real_name] tonight."))
|
||||
current_target = target
|
||||
|
||||
/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!current_target)
|
||||
return
|
||||
var/datum/mafia_role/target = current_target
|
||||
current_target = null
|
||||
if(!target.can_action(game, src, "communion"))
|
||||
return
|
||||
if(target)
|
||||
to_chat(body,"<span class='warning'>You invoke spirit of [target.body.real_name] and learn their role was <b>[target.name]<b>.</span>")
|
||||
to_chat(body,span_warning("You invoke spirit of [target.body.real_name] and learn their role was <b>[target.name]<b>."))
|
||||
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
|
||||
current_target = null
|
||||
|
||||
/datum/mafia_role/md
|
||||
name = "Medical Doctor"
|
||||
desc = "You can protect a single person each night from killing."
|
||||
revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
revealed_outfit = /datum/outfit/mafia/md
|
||||
role_type = TOWN_PROTECT
|
||||
hud_icon = "hudmedicaldoctor"
|
||||
revealed_icon = "medicaldoctor"
|
||||
@@ -271,29 +305,38 @@
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(target.name == "Head of Personnel" && target.revealed)
|
||||
if((target.role_flags & ROLE_VULNERABLE) && (target.role_flags & ROLE_REVEALED)) //do not give the option to protect roles that your protection will fail on
|
||||
return FALSE
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(!target || target.game_status != MAFIA_ALIVE)
|
||||
to_chat(body,"<span class='warning'>You can only protect alive people.</span>")
|
||||
to_chat(body,span_warning("You can only protect alive people."))
|
||||
return
|
||||
to_chat(body,"<span class='warning'>You will protect [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will protect [target.body.real_name] tonight."))
|
||||
current_protected = target
|
||||
|
||||
/datum/mafia_role/md/proc/protect(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(current_protected)
|
||||
RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill)
|
||||
add_note("N[game.turn] - Protected [current_protected.body.real_name]")
|
||||
if(!current_protected)
|
||||
return
|
||||
var/datum/mafia_role/target = current_protected
|
||||
//current protected is unset at the end, as this action ends at a different phase
|
||||
if(!target.can_action(game, src, "medical assistance"))
|
||||
return
|
||||
|
||||
/datum/mafia_role/md/proc/prevent_kill(datum/source)
|
||||
RegisterSignal(target,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill)
|
||||
add_note("N[game.turn] - Protected [target.body.real_name]")
|
||||
|
||||
/datum/mafia_role/md/proc/prevent_kill(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
to_chat(body,"<span class='warning'>The person you protected tonight was attacked!</span>")
|
||||
to_chat(current_protected.body,"<span class='userdanger'>You were attacked last night, but someone nursed you back to life!</span>")
|
||||
if((current_protected.role_flags & ROLE_VULNERABLE))
|
||||
to_chat(body,span_warning("The person you protected could not be saved."))
|
||||
return
|
||||
to_chat(body,span_warning("The person you protected tonight was attacked!"))
|
||||
to_chat(current_protected.body,span_greentext("You were attacked last night, but someone nursed you back to life!"))
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game)
|
||||
@@ -303,11 +346,77 @@
|
||||
UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL)
|
||||
current_protected = null
|
||||
|
||||
/datum/mafia_role/officer
|
||||
name = "Security Officer"
|
||||
desc = "You can protect a single person each night. If they are attacked, you will retaliate, killing yourself and the attacker."
|
||||
revealed_outfit = /datum/outfit/mafia/security
|
||||
revealed_icon = "securityofficer"
|
||||
hud_icon = "hudsecurityofficer"
|
||||
role_type = TOWN_PROTECT
|
||||
role_flags = ROLE_CAN_KILL
|
||||
winner_award = /datum/award/achievement/mafia/officer
|
||||
|
||||
targeted_actions = list("Defend")
|
||||
var/datum/mafia_role/current_defended
|
||||
|
||||
/datum/mafia_role/officer/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/defend)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/end_defense)
|
||||
|
||||
/datum/mafia_role/officer/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if((role_flags & ROLE_VULNERABLE) && (target.role_flags & ROLE_REVEALED)) //do not give the option to protect roles that your protection will fail on
|
||||
return FALSE
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/officer/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(!target || target.game_status != MAFIA_ALIVE)
|
||||
to_chat(body,span_warning("You can only defend alive people."))
|
||||
return
|
||||
to_chat(body,span_warning("You will defend [target.body.real_name] tonight."))
|
||||
current_defended = target
|
||||
|
||||
/datum/mafia_role/officer/proc/defend(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!current_defended)
|
||||
return
|
||||
var/datum/mafia_role/target = current_defended
|
||||
//current defended is unset at the end, as this action ends at a different phase
|
||||
if(!target.can_action(game, src, "security patrol"))
|
||||
return
|
||||
if(target)
|
||||
RegisterSignal(target,COMSIG_MAFIA_ON_KILL,.proc/retaliate)
|
||||
add_note("N[game.turn] - Defended [target.body.real_name]")
|
||||
|
||||
/datum/mafia_role/officer/proc/retaliate(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if((current_defended.role_flags & ROLE_VULNERABLE))
|
||||
to_chat(body,span_warning("The person you defended could not be saved. You could not attack the killer."))
|
||||
return
|
||||
to_chat(body,span_userdanger("The person you defended tonight was attacked!"))
|
||||
to_chat(current_defended.body,span_userdanger("You were attacked last night, but security fought off the attacker!"))
|
||||
if(attacker.kill(game,src,FALSE)) //you attack the attacker
|
||||
to_chat(attacker.body, span_userdanger("You have been ambushed by Security!"))
|
||||
kill(game,attacker,FALSE) //the attacker attacks you, they were able to attack the target so they can attack you.
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/officer/proc/end_defense(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(current_defended)
|
||||
UnregisterSignal(current_defended,COMSIG_MAFIA_ON_KILL)
|
||||
current_defended = null
|
||||
|
||||
/datum/mafia_role/lawyer
|
||||
name = "Lawyer"
|
||||
desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions."
|
||||
revealed_outfit = /datum/outfit/mafia/lawyer
|
||||
role_type = TOWN_PROTECT
|
||||
role_type = TOWN_SUPPORT
|
||||
hud_icon = "hudlawyer"
|
||||
revealed_icon = "lawyer"
|
||||
winner_award = /datum/award/achievement/mafia/lawyer
|
||||
@@ -317,23 +426,30 @@
|
||||
|
||||
/datum/mafia_role/lawyer/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock_text)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_START,.proc/try_to_roleblock)
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release)
|
||||
|
||||
/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game)
|
||||
/datum/mafia_role/lawyer/proc/roleblock(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer.
|
||||
if(!current_target)
|
||||
return
|
||||
|
||||
var/datum/mafia_role/target = current_target
|
||||
if(!target.can_action(game, src, "roleblock")) //roleblocking a warden moment
|
||||
current_target = null
|
||||
if(current_target)
|
||||
to_chat(current_target.body,"<span class='big bold red'>YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.</span>")
|
||||
add_note("N[game.turn] - [current_target.body.real_name] - Blocked")
|
||||
return
|
||||
|
||||
to_chat(target.body,"<span class='big bold red'>YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Blocked")
|
||||
target.role_flags |= ROLE_ROLEBLOCKED
|
||||
|
||||
/datum/mafia_role/lawyer/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(target == src)
|
||||
return FALSE
|
||||
if(game.phase == MAFIA_PHASE_NIGHT)
|
||||
return FALSE
|
||||
if(target.game_status != MAFIA_ALIVE)
|
||||
@@ -343,53 +459,164 @@
|
||||
. = ..()
|
||||
if(target == current_target)
|
||||
current_target = null
|
||||
to_chat(body,"<span class='warning'>You have decided against blocking anyone tonight.</span>")
|
||||
to_chat(body,span_warning("You have decided against blocking anyone tonight."))
|
||||
else
|
||||
current_target = target
|
||||
to_chat(body,"<span class='warning'>You will block [target.body.real_name] tonight.</span>")
|
||||
|
||||
/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(current_target)
|
||||
RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action)
|
||||
to_chat(body,span_warning("You will block [target.body.real_name] tonight."))
|
||||
|
||||
/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
. = ..()
|
||||
if(current_target)
|
||||
UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION)
|
||||
current_target.role_flags &= ~ROLE_ROLEBLOCKED
|
||||
current_target = null
|
||||
|
||||
/datum/mafia_role/lawyer/proc/prevent_action(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge
|
||||
return MAFIA_PREVENT_ACTION
|
||||
|
||||
/datum/mafia_role/hop
|
||||
name = "Head of Personnel"
|
||||
desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!"
|
||||
revealed_outfit = /datum/outfit/mafia/hop
|
||||
role_type = TOWN_MISC
|
||||
role_type = TOWN_SUPPORT
|
||||
role_flags = ROLE_UNIQUE
|
||||
hud_icon = "hudheadofpersonnel"
|
||||
revealed_icon = "headofpersonnel"
|
||||
revealed_outfit = /datum/outfit/mafia/hop
|
||||
winner_award = /datum/award/achievement/mafia/hop
|
||||
|
||||
targeted_actions = list("Reveal")
|
||||
vote_potential = 3
|
||||
|
||||
/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed)
|
||||
if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || (role_flags & ROLE_REVEALED))
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
reveal_role(game, TRUE)
|
||||
vote_power = 2
|
||||
role_flags |= ROLE_VULNERABLE
|
||||
vote_power = 3
|
||||
|
||||
///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win
|
||||
/datum/mafia_role/hos
|
||||
name = "Head of Security"
|
||||
desc = "You can decide to execute during the night, visiting someone killing, and revealing them. If they are innocent, you will die at the start of the next night."
|
||||
role_type = TOWN_KILLING
|
||||
role_flags = ROLE_CAN_KILL | ROLE_UNIQUE
|
||||
revealed_outfit = /datum/outfit/mafia/hos
|
||||
revealed_icon = "headofsecurity"
|
||||
hud_icon = "hudheadofsecurity"
|
||||
winner_award = /datum/award/achievement/mafia/hos
|
||||
|
||||
targeted_actions = list("Execute")
|
||||
var/datum/mafia_role/execute_target
|
||||
|
||||
/datum/mafia_role/hos/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/execute)
|
||||
|
||||
/datum/mafia_role/hos/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/hos/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(execute_target == target)
|
||||
to_chat(body,span_warning("You have decided against executing tonight."))
|
||||
to_chat(body,span_warning("You have decided to execute [target.body.real_name] tonight."))
|
||||
execute_target = target
|
||||
|
||||
/datum/mafia_role/hos/proc/execute(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!execute_target)
|
||||
return
|
||||
var/datum/mafia_role/target = execute_target
|
||||
execute_target = null
|
||||
if(!target.can_action(game, src, "execution")) //roleblocking a warden moment
|
||||
return
|
||||
if(!target.kill(game,src,FALSE))//protection
|
||||
to_chat(body,span_danger("Your attempt at executing [target.body.real_name] was prevented, or [target.body.real_name] is immune!"))
|
||||
else
|
||||
to_chat(target.body, span_userdanger("You have been executed by the Head of Security!"))
|
||||
target.reveal_role(game, verbose = TRUE)
|
||||
if(target.team == MAFIA_TEAM_TOWN)
|
||||
to_chat(body,span_userdanger("You have killed an innocent crewmember. You will die tomorrow night."))
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/internal_affairs)
|
||||
role_flags |= ROLE_VULNERABLE
|
||||
|
||||
/datum/mafia_role/hos/proc/internal_affairs(datum/mafia_controller/game)
|
||||
to_chat(body,span_userdanger("You have been killed by Nanotrasen Internal Affairs!"))
|
||||
reveal_role(game, verbose = TRUE)
|
||||
kill(game,src,FALSE) //you technically kill yourself but that shouldn't matter
|
||||
|
||||
|
||||
//just helps read better
|
||||
#define WARDEN_NOT_LOCKDOWN 0//will NOT kill visitors tonight
|
||||
#define WARDEN_WILL_LOCKDOWN 1 //will kill visitors tonight
|
||||
|
||||
/datum/mafia_role/warden
|
||||
name = "Warden"
|
||||
desc = "You can lockdown during the night once, killing any visitors. WARNING: This kills fellow town members, too!"
|
||||
|
||||
role_type = TOWN_KILLING
|
||||
role_flags = ROLE_CAN_KILL
|
||||
revealed_outfit = /datum/outfit/mafia/warden
|
||||
revealed_icon = "warden"
|
||||
hud_icon = "hudwarden"
|
||||
winner_award = /datum/award/achievement/mafia/warden
|
||||
|
||||
actions = list("Lockdown")
|
||||
var/charges = 1
|
||||
var/protection_status = WARDEN_NOT_LOCKDOWN
|
||||
|
||||
|
||||
/datum/mafia_role/warden/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/night_start)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/night_end)
|
||||
|
||||
/datum/mafia_role/warden/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!charges)
|
||||
to_chat(body,span_danger("You've already locked down this game!"))
|
||||
return
|
||||
if(game.phase == MAFIA_PHASE_NIGHT)
|
||||
to_chat(body,span_danger("You don't have time to lockdown, night has already arrived."))
|
||||
return
|
||||
if(protection_status == WARDEN_WILL_LOCKDOWN)
|
||||
to_chat(body,span_danger("You decide to not lockdown tonight."))
|
||||
else
|
||||
to_chat(body,span_danger("You decide to lockdown, killing any visitors."))
|
||||
protection_status = !protection_status
|
||||
|
||||
/datum/mafia_role/warden/proc/night_start(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(protection_status == WARDEN_WILL_LOCKDOWN)
|
||||
to_chat(body,span_danger("Any and all visitors are going to eat buckshot tonight."))
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_VISIT,.proc/self_defense)
|
||||
|
||||
/datum/mafia_role/warden/proc/night_end(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(protection_status == WARDEN_WILL_LOCKDOWN)
|
||||
charges--
|
||||
UnregisterSignal(src,COMSIG_MAFIA_ON_KILL)
|
||||
to_chat(body,span_danger("You are no longer protected. You have used up your power."))
|
||||
protection_status = WARDEN_NOT_LOCKDOWN
|
||||
|
||||
/datum/mafia_role/warden/proc/self_defense(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
to_chat(body,span_userdanger("You have shot a visitor!"))
|
||||
to_chat(attacker,span_userdanger("You have visited the warden!"))
|
||||
attacker.kill(game, src, lynch = FALSE)
|
||||
return MAFIA_VISIT_INTERRUPTED
|
||||
|
||||
#undef WARDEN_NOT_LOCKDOWN
|
||||
#undef WARDEN_WILL_LOCKDOWN
|
||||
|
||||
///MAFIA ROLES/// they're the "anti-town" working to kill off townies to win
|
||||
|
||||
/datum/mafia_role/mafia
|
||||
name = "Changeling"
|
||||
@@ -436,7 +663,7 @@
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
to_chat(body,"<span class='warning'>You will feast on the memories of [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will feast on the memories of [target.body.real_name] tonight."))
|
||||
current_investigation = target
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game)
|
||||
@@ -444,18 +671,15 @@
|
||||
|
||||
var/datum/mafia_role/target = current_investigation
|
||||
current_investigation = null
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION)
|
||||
to_chat(body,"<span class='warning'>You were unable to investigate [target.body.real_name].</span>")
|
||||
if(!target.can_action(game, src, "thought feeding"))
|
||||
add_note("N[game.turn] - [target.body.real_name] - Unable to investigate")
|
||||
return
|
||||
if(target)
|
||||
if(target.detect_immune)
|
||||
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the Assistant.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Assistant")
|
||||
else
|
||||
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the [target.name].</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
|
||||
|
||||
if((target.role_flags & ROLE_UNDETECTABLE))
|
||||
to_chat(body,span_warning("[target.body.real_name]'s memories reveal that they are the Assistant."))
|
||||
add_note("N[game.turn] - [target.body.real_name] - Assistant")
|
||||
else
|
||||
to_chat(body,span_warning("[target.body.real_name]'s memories reveal that they are the [target.name]."))
|
||||
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
|
||||
|
||||
///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone.
|
||||
|
||||
@@ -465,15 +689,14 @@
|
||||
win_condition = "kill everyone."
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_KILL
|
||||
role_flags = ROLE_CAN_KILL
|
||||
winner_award = /datum/award/achievement/mafia/traitor
|
||||
|
||||
targeted_actions = list("Night Kill")
|
||||
revealed_outfit = /datum/outfit/mafia/traitor
|
||||
|
||||
hud_icon = "hudtraitor"
|
||||
revealed_icon = "traitor"
|
||||
hud_icon = "hudtraitor"
|
||||
special_theme = "neutral"
|
||||
|
||||
targeted_actions = list("Night Kill")
|
||||
var/datum/mafia_role/current_victim
|
||||
|
||||
/datum/mafia_role/traitor/New(datum/mafia_controller/game)
|
||||
@@ -481,17 +704,17 @@
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/nightkill_immunity)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/try_to_kill)
|
||||
|
||||
/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead
|
||||
/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead, they cannot be stopped by killing roles anyways
|
||||
return alive_town + alive_mafia <= 1
|
||||
|
||||
/datum/mafia_role/traitor/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead
|
||||
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
|
||||
|
||||
/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch)
|
||||
/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(game.phase == MAFIA_PHASE_NIGHT && !lynch)
|
||||
to_chat(body,"<span class='userdanger'>You were attacked, but they'll have to try harder than that to put you down.</span>")
|
||||
to_chat(body,span_userdanger("You were attacked, but they'll have to try harder than that to put you down."))
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/traitor/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
@@ -504,25 +727,29 @@
|
||||
/datum/mafia_role/traitor/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
current_victim = target
|
||||
to_chat(body,"<span class='warning'>You will attempt to kill [target.body.real_name] tonight.</span>")
|
||||
to_chat(body,span_warning("You will attempt to kill [target.body.real_name] tonight."))
|
||||
|
||||
/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source)
|
||||
// SIGNAL_HANDLER
|
||||
/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!current_victim)
|
||||
return
|
||||
var/datum/mafia_role/target = current_victim
|
||||
current_victim = null
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION)
|
||||
if(!target.can_action(game, src, "flickering")) //flickering a warden
|
||||
return
|
||||
if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE)
|
||||
if(!target.kill(source))
|
||||
to_chat(body,"<span class='danger'>Your attempt at killing [target.body] was prevented!</span>")
|
||||
if(game_status == MAFIA_ALIVE)
|
||||
if(!target.kill(game,src,FALSE))
|
||||
to_chat(body,span_danger("Your attempt at killing [target.body.real_name] was prevented!"))
|
||||
else
|
||||
to_chat(target.body, span_userdanger("You have been killed by a Traitor!"))
|
||||
|
||||
/datum/mafia_role/nightmare
|
||||
name = "Nightmare"
|
||||
desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win."
|
||||
desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night, becoming immune to attacks from those roles. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win."
|
||||
win_condition = "kill everyone."
|
||||
revealed_outfit = /datum/outfit/mafia/nightmare
|
||||
detect_immune = TRUE
|
||||
role_flags = ROLE_UNDETECTABLE | ROLE_CAN_KILL
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_KILL
|
||||
special_theme = "neutral"
|
||||
@@ -536,6 +763,7 @@
|
||||
|
||||
/datum/mafia_role/nightmare/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/flickering_immunity)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt)
|
||||
|
||||
/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead
|
||||
@@ -562,33 +790,42 @@
|
||||
/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(target == flicker_target)
|
||||
to_chat(body,"<span class='warning'>You will do nothing tonight.</span>")
|
||||
to_chat(body,span_warning("You will do nothing tonight."))
|
||||
flicker_target = null
|
||||
flicker_target = target
|
||||
if(action == "Flicker")
|
||||
to_chat(body,"<span class='warning'>You will attempt to flicker [target.body.real_name]'s room tonight.</span>")
|
||||
to_chat(body,span_warning("You will attempt to flicker [target.body.real_name]'s room tonight."))
|
||||
else
|
||||
to_chat(body,"<span class='danger'>You will hunt everyone in a flickering room down tonight.</span>")
|
||||
to_chat(body,span_danger("You will hunt everyone in a flickering room down tonight."))
|
||||
|
||||
/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source)
|
||||
// SIGNAL_HANDLER
|
||||
/datum/mafia_role/nightmare/proc/flickering_immunity(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
if(!attacker)
|
||||
return //no chance man, that's a town lynch
|
||||
|
||||
if(game_status != MAFIA_ALIVE || !flicker_target)
|
||||
return
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION)
|
||||
to_chat(flicker_target.body, "<span class='warning'>Your actions were prevented!</span>")
|
||||
if(attacker in flickering)
|
||||
to_chat(body,span_userdanger("You were attacked by someone in a flickering room. You have danced in the shadows, evading them."))
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!flicker_target)
|
||||
return
|
||||
var/datum/mafia_role/target = flicker_target
|
||||
flicker_target = null
|
||||
if(!target.can_action(game, src, "flickering")) //flickering a warden
|
||||
return
|
||||
|
||||
if(target != src) //flicker instead of hunt
|
||||
to_chat(target.body, "<span class='userdanger'>The lights begin to flicker and dim. You're in danger.</span>")
|
||||
to_chat(target.body, span_userdanger("The lights begin to flicker and dim. You're in danger."))
|
||||
flickering += target
|
||||
return
|
||||
for(var/r in flickering)
|
||||
var/datum/mafia_role/role = r
|
||||
if(role && role.game_status == MAFIA_ALIVE)
|
||||
to_chat(role.body, "<span class='userdanger'>A shadowy monster appears out of the darkness!</span>")
|
||||
role.kill(source)
|
||||
to_chat(role.body, span_userdanger("A shadowy figure appears out of the darkness!"))
|
||||
role.kill(game,src,FALSE)
|
||||
flickering -= role
|
||||
|
||||
//just helps read better
|
||||
@@ -599,7 +836,6 @@
|
||||
name = "Fugitive"
|
||||
desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone."
|
||||
win_condition = "survive to the end of the game, with anyone"
|
||||
solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town
|
||||
revealed_outfit = /datum/outfit/mafia/fugitive
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
@@ -622,22 +858,22 @@
|
||||
/datum/mafia_role/fugitive/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!charges)
|
||||
to_chat(body,"<span class='danger'>You're out of supplies and cannot protect yourself anymore.</span>")
|
||||
to_chat(body,span_danger("You're out of supplies and cannot protect yourself anymore."))
|
||||
return
|
||||
if(game.phase == MAFIA_PHASE_NIGHT)
|
||||
to_chat(body,"<span class='danger'>You don't have time to prepare, night has already arrived.</span>")
|
||||
to_chat(body,span_danger("You don't have time to prepare, night has already arrived."))
|
||||
return
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
to_chat(body,"<span class='danger'>You decide to not prepare tonight.</span>")
|
||||
to_chat(body,span_danger("You decide to not prepare tonight."))
|
||||
else
|
||||
to_chat(body,"<span class='danger'>You decide to prepare for a horrible night.</span>")
|
||||
to_chat(body,span_danger("You decide to prepare for a horrible night."))
|
||||
protection_status = !protection_status
|
||||
|
||||
/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
to_chat(body,"<span class='danger'>Your preparations are complete. Nothing could kill you tonight!</span>")
|
||||
to_chat(body,span_danger("Your preparations are complete. Nothing could kill you tonight!"))
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death)
|
||||
|
||||
/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game)
|
||||
@@ -646,13 +882,13 @@
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
charges--
|
||||
UnregisterSignal(src,COMSIG_MAFIA_ON_KILL)
|
||||
to_chat(body,"<span class='danger'>You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power.</span>")
|
||||
to_chat(body,span_danger("You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power."))
|
||||
protection_status = FUGITIVE_NOT_PRESERVING
|
||||
|
||||
/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game)
|
||||
/datum/mafia_role/fugitive/proc/prevent_death(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
to_chat(body,"<span class='userdanger'>You were attacked! Luckily, you were ready for this!</span>")
|
||||
to_chat(body,span_userdanger("You were attacked! Luckily, you were ready for this!"))
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game)
|
||||
@@ -669,8 +905,7 @@
|
||||
name = "Obsessed"
|
||||
desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!"
|
||||
win_condition = "lynch their obsession."
|
||||
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
|
||||
revealed_outfit = /datum/outfit/mafia/obsessed
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
special_theme = "neutral"
|
||||
@@ -678,9 +913,6 @@
|
||||
revealed_icon = "obsessed"
|
||||
|
||||
winner_award = /datum/award/achievement/mafia/obsessed
|
||||
|
||||
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
|
||||
var/datum/mafia_role/obsession
|
||||
var/lynched_target = FALSE
|
||||
|
||||
@@ -700,12 +932,12 @@
|
||||
if(!obsession)
|
||||
obsession = pick(all_roles_shuffle) //okay no town just pick anyone here
|
||||
//if you still don't have an obsession you're playing a single player game like i can't help your dumb ass
|
||||
to_chat(body, "<span class='userdanger'>Your obsession is [obsession.body.real_name]! Get them lynched to win!</span>")
|
||||
to_chat(body, span_userdanger("Your obsession is [obsession.body.real_name]! Get them lynched to win!"))
|
||||
add_note("N[game.turn] - I vowed to watch my obsession, [obsession.body.real_name], hang!") //it'll always be N1 but whatever
|
||||
RegisterSignal(obsession,COMSIG_MAFIA_ON_KILL,.proc/check_victory)
|
||||
UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN)
|
||||
|
||||
/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch)
|
||||
/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
UnregisterSignal(source,COMSIG_MAFIA_ON_KILL)
|
||||
@@ -716,14 +948,13 @@
|
||||
game.award_role(winner_award, src)
|
||||
reveal_role(game, FALSE)
|
||||
else
|
||||
to_chat(body, "<span class='userdanger'>You have failed your objective to lynch [obsession.body]!</span>")
|
||||
to_chat(body, span_userdanger("You have failed your objective to lynch [obsession.body.real_name]!"))
|
||||
|
||||
/datum/mafia_role/clown
|
||||
name = "Clown"
|
||||
desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!"
|
||||
win_condition = "get themselves lynched!"
|
||||
revealed_outfit = /datum/outfit/mafia/clown
|
||||
solo_counts_as_town = TRUE
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
special_theme = "neutral"
|
||||
@@ -735,8 +966,8 @@
|
||||
. = ..()
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank)
|
||||
|
||||
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch)
|
||||
// SIGNAL_HANDLER
|
||||
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,datum/mafia_role/attacker,lynch)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(lynch)
|
||||
var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes)
|
||||
|
||||
@@ -8,61 +8,73 @@
|
||||
..()
|
||||
return late ? INITIALIZE_HINT_LATELOAD : INITIALIZE_HINT_QDEL
|
||||
|
||||
|
||||
//airlock helpers
|
||||
/obj/effect/mapping_helpers/airlock
|
||||
layer = DOOR_HELPER_LAYER
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(!airlock)
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
else
|
||||
payload(airlock)
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/proc/payload(obj/machinery/door/airlock/payload)
|
||||
return
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper
|
||||
name = "airlock cyclelink helper"
|
||||
icon_state = "airlock_cyclelink_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.cyclelinkeddir)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
else
|
||||
airlock.cyclelinkeddir = dir
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.cyclelinkeddir)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.cyclelinkeddir = dir
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi
|
||||
name = "airlock multi-cyclelink helper"
|
||||
icon_state = "airlock_multicyclelink_helper"
|
||||
var/cycle_id
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.closeOtherId)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] closeOtherId, but it's already set!")
|
||||
else
|
||||
airlock.closeOtherId = cycle_id
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/locked
|
||||
name = "airlock lock helper"
|
||||
icon_state = "airlock_locked_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/locked/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.locked)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
else
|
||||
airlock.locked = TRUE
|
||||
/obj/effect/mapping_helpers/airlock/locked/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.locked)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.locked = TRUE
|
||||
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/unres
|
||||
name = "airlock unresctricted side helper"
|
||||
icon_state = "airlock_unres_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
airlock.unres_sides ^= dir
|
||||
/obj/effect/mapping_helpers/airlock/unres/payload(obj/machinery/door/airlock/airlock)
|
||||
airlock.unres_sides ^= dir
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/abandoned
|
||||
name = "airlock abandoned helper"
|
||||
icon_state = "airlock_abandoned"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/abandoned/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.abandoned)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to make [airlock] abandoned but it's already abandoned!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.abandoned = TRUE
|
||||
|
||||
|
||||
//needs to do its thing before spawn_rivers() is called
|
||||
@@ -79,9 +91,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
//This helper applies components to things on the map directly.
|
||||
/obj/effect/mapping_helpers/component_injector
|
||||
name = "Component Injector"
|
||||
icon_state = "component"
|
||||
late = TRUE
|
||||
var/target_type
|
||||
var/target_name
|
||||
var/all = FALSE //Will inject into all fitting the criteria if true, otherwise first found
|
||||
var/target_type //Will inject into atoms of this type
|
||||
var/target_name //Will inject into atoms with this name
|
||||
var/component_type
|
||||
|
||||
//Late init so everything is likely ready and loaded (no warranty)
|
||||
@@ -98,8 +112,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
continue
|
||||
var/cargs = build_args()
|
||||
A._AddComponent(cargs)
|
||||
if(!all)
|
||||
qdel(src)
|
||||
return
|
||||
if(all)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/effect/mapping_helpers/component_injector/proc/build_args()
|
||||
return list(component_type)
|
||||
@@ -115,3 +132,276 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
CRASH("Wrong disease type passed in.")
|
||||
var/datum/disease/D = new disease_type()
|
||||
return list(component_type,D)
|
||||
|
||||
// /obj/effect/mapping_helpers/component_injector/areabound
|
||||
// name = "Areabound Injector"
|
||||
// icon_state = "component_areabound"
|
||||
// component_type = /datum/component/areabound
|
||||
// target_type = /atom/movable
|
||||
|
||||
/obj/effect/mapping_helpers/dead_body_placer
|
||||
name = "Dead Body placer"
|
||||
late = TRUE
|
||||
icon_state = "deadbodyplacer"
|
||||
var/bodycount = 2 //number of bodies to spawn
|
||||
|
||||
/obj/effect/mapping_helpers/dead_body_placer/LateInitialize()
|
||||
var/area/a = get_area(src)
|
||||
var/list/trays = list()
|
||||
for (var/i in a.contents)
|
||||
if (istype(i, /obj/structure/bodycontainer/morgue))
|
||||
trays += i
|
||||
if(!trays.len)
|
||||
log_mapping("[src] at [x],[y] could not find any morgues.")
|
||||
return
|
||||
for (var/i = 1 to bodycount)
|
||||
var/obj/structure/bodycontainer/morgue/j = pick(trays)
|
||||
var/mob/living/carbon/human/h = new /mob/living/carbon/human(j, 1)
|
||||
h.death()
|
||||
for (var/part in h.internal_organs) //randomly remove organs from each body, set those we keep to be in stasis
|
||||
if (prob(40))
|
||||
qdel(part)
|
||||
else
|
||||
var/obj/item/organ/O = part
|
||||
O.organ_flags |= ORGAN_FROZEN
|
||||
j.update_appearance()
|
||||
qdel(src)
|
||||
|
||||
|
||||
//On Ian's birthday, the hop's office is decorated.
|
||||
/obj/effect/mapping_helpers/ianbirthday
|
||||
name = "Ian's Bday Helper"
|
||||
late = TRUE
|
||||
icon_state = "iansbdayhelper"
|
||||
var/balloon_clusters = 2
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/LateInitialize()
|
||||
if(locate(/datum/holiday/ianbirthday) in SSevents.holidays)
|
||||
birthday()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/proc/birthday()
|
||||
var/area/a = get_area(src)
|
||||
var/list/table = list()//should only be one aka the front desk, but just in case...
|
||||
var/list/openturfs = list()
|
||||
|
||||
//confetti and a corgi balloon! (and some list stuff for more decorations)
|
||||
for(var/thing in a.contents)
|
||||
if(istype(thing, /obj/structure/table/reinforced))
|
||||
table += thing
|
||||
if(isopenturf(thing))
|
||||
// new /obj/effect/decal/cleanable/confetti(thing)
|
||||
// if(locate(/obj/structure/bed/dogbed/ian) in thing)
|
||||
// new /obj/item/toy/balloon/corgi(thing)
|
||||
// else
|
||||
openturfs += thing
|
||||
|
||||
//cake + knife to cut it!
|
||||
if(length(table))
|
||||
var/turf/food_turf = get_turf(pick(table))
|
||||
new /obj/item/kitchen/knife(food_turf)
|
||||
var/obj/item/reagent_containers/food/snacks/store/cake/birthday/iancake = new(food_turf)
|
||||
iancake.desc = "Happy birthday, Ian!"
|
||||
// remind me to give ian proper baloons!
|
||||
//some balloons! this picks an open turf and pops a few balloons in and around that turf, yay.
|
||||
// for(var/i in 1 to balloon_clusters)
|
||||
// var/turf/clusterspot = pick_n_take(openturfs)
|
||||
// new /obj/item/toy/balloon(clusterspot)
|
||||
// var/balloons_left_to_give = 3 //the amount of balloons around the cluster
|
||||
// var/list/dirs_to_balloon = GLOB.cardinals.Copy()
|
||||
// while(balloons_left_to_give > 0)
|
||||
// balloons_left_to_give--
|
||||
// var/chosen_dir = pick_n_take(dirs_to_balloon)
|
||||
// var/turf/balloonstep = get_step(clusterspot, chosen_dir)
|
||||
// var/placed = FALSE
|
||||
// if(isopenturf(balloonstep))
|
||||
// var/obj/item/toy/balloon/B = new(balloonstep)//this clumps the cluster together
|
||||
// placed = TRUE
|
||||
// if(chosen_dir == NORTH)
|
||||
// B.pixel_y -= 10
|
||||
// if(chosen_dir == SOUTH)
|
||||
// B.pixel_y += 10
|
||||
// if(chosen_dir == EAST)
|
||||
// B.pixel_x -= 10
|
||||
// if(chosen_dir == WEST)
|
||||
// B.pixel_x += 10
|
||||
// if(!placed)
|
||||
// new /obj/item/toy/balloon(clusterspot)
|
||||
//remind me to add wall decor!
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/admin//so admins may birthday any room
|
||||
name = "generic birthday setup"
|
||||
icon_state = "bdayhelper"
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/admin/LateInitialize()
|
||||
birthday()
|
||||
qdel(src)
|
||||
|
||||
//Ian, like most dogs, loves a good new years eve party.
|
||||
/obj/effect/mapping_helpers/iannewyear
|
||||
name = "Ian's New Years Helper"
|
||||
late = TRUE
|
||||
icon_state = "iansnewyrshelper"
|
||||
|
||||
/obj/effect/mapping_helpers/iannewyear/LateInitialize()
|
||||
if(SSevents.holidays && SSevents.holidays[NEW_YEAR])
|
||||
fireworks()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/iannewyear/proc/fireworks()
|
||||
var/area/a = get_area(src)
|
||||
var/list/table = list()//should only be one aka the front desk, but just in case...
|
||||
var/list/openturfs = list()
|
||||
|
||||
for(var/thing in a.contents)
|
||||
if(istype(thing, /obj/structure/table/reinforced))
|
||||
table += thing
|
||||
else if(isopenturf(thing))
|
||||
if(locate(/obj/structure/bed/dogbed/ian) in thing)
|
||||
new /obj/item/clothing/head/festive(thing)
|
||||
var/obj/item/reagent_containers/food/drinks/bottle/champagne/iandrink = new(thing)
|
||||
iandrink.name = "dog champagne"
|
||||
iandrink.pixel_y += 8
|
||||
iandrink.pixel_x += 8
|
||||
else
|
||||
openturfs += thing
|
||||
|
||||
var/turf/fireworks_turf = get_turf(pick(table))
|
||||
var/obj/item/storage/box/matches/matchbox = new(fireworks_turf)
|
||||
matchbox.pixel_y += 8
|
||||
matchbox.pixel_x -= 3
|
||||
// new /obj/item/storage/box/fireworks/dangerous(fireworks_turf) //dangerous version for extra holiday memes.
|
||||
|
||||
//lets mappers place notes on airlocks with custom info or a pre-made note from a path
|
||||
/obj/effect/mapping_helpers/airlock_note_placer
|
||||
name = "Airlock Note Placer"
|
||||
late = TRUE
|
||||
icon_state = "airlocknoteplacer"
|
||||
var/note_info //for writing out custom notes without creating an extra paper subtype
|
||||
var/note_name //custom note name
|
||||
var/note_path //if you already have something wrote up in a paper subtype, put the path here
|
||||
|
||||
/obj/effect/mapping_helpers/airlock_note_placer/LateInitialize()
|
||||
var/turf/turf = get_turf(src)
|
||||
if(note_path && !istype(note_path, /obj/item/paper)) //don't put non-paper in the paper slot thank you
|
||||
log_mapping("[src] at [x],[y] had an improper note_path path, could not place paper note.")
|
||||
qdel(src)
|
||||
if(locate(/obj/machinery/door/airlock) in turf)
|
||||
var/obj/machinery/door/airlock/found_airlock = locate(/obj/machinery/door/airlock) in turf
|
||||
if(note_path)
|
||||
found_airlock.note = note_path
|
||||
found_airlock.update_appearance()
|
||||
qdel(src)
|
||||
if(note_info)
|
||||
var/obj/item/paper/paper = new /obj/item/paper(src)
|
||||
if(note_name)
|
||||
paper.name = note_name
|
||||
paper.info = "[note_info]"
|
||||
found_airlock.note = paper
|
||||
paper.forceMove(found_airlock)
|
||||
found_airlock.update_appearance()
|
||||
qdel(src)
|
||||
log_mapping("[src] at [x],[y] had no note_path or note_info, cannot place paper note.")
|
||||
qdel(src)
|
||||
log_mapping("[src] at [x],[y] could not find an airlock on current turf, cannot place paper note.")
|
||||
qdel(src)
|
||||
|
||||
//This helper applies traits to things on the map directly.
|
||||
/obj/effect/mapping_helpers/trait_injector
|
||||
name = "Trait Injector"
|
||||
icon_state = "trait"
|
||||
late = TRUE
|
||||
///Will inject into all fitting the criteria if false, otherwise first found.
|
||||
var/first_match_only = TRUE
|
||||
///Will inject into atoms of this type.
|
||||
var/target_type
|
||||
///Will inject into atoms with this name.
|
||||
var/target_name
|
||||
///Name of the trait, in the lower-case text (NOT the upper-case define) form.
|
||||
var/trait_name
|
||||
|
||||
//Late init so everything is likely ready and loaded (no warranty)
|
||||
/obj/effect/mapping_helpers/trait_injector/LateInitialize()
|
||||
if(!GLOB.trait_name_map)
|
||||
GLOB.trait_name_map = generate_trait_name_map()
|
||||
if(!GLOB.trait_name_map.Find(trait_name))
|
||||
CRASH("Wrong trait in [type] - [trait_name] is not a trait")
|
||||
var/turf/target_turf = get_turf(src)
|
||||
var/matches_found = 0
|
||||
for(var/a in target_turf.GetAllContents())
|
||||
var/atom/atom_on_turf = a
|
||||
if(atom_on_turf == src)
|
||||
continue
|
||||
if(target_name && atom_on_turf.name != target_name)
|
||||
continue
|
||||
if(target_type && !istype(atom_on_turf,target_type))
|
||||
continue
|
||||
ADD_TRAIT(atom_on_turf, trait_name, MAPPING_HELPER_TRAIT)
|
||||
matches_found++
|
||||
if(first_match_only)
|
||||
qdel(src)
|
||||
return
|
||||
if(!matches_found)
|
||||
stack_trace("Trait mapper found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name] | trait name: [trait_name]")
|
||||
qdel(src)
|
||||
|
||||
/// Fetches an external dmi and applies to the target object
|
||||
/obj/effect/mapping_helpers/custom_icon
|
||||
name = "Custom Icon Helper"
|
||||
icon_state = "trait"
|
||||
late = TRUE
|
||||
///Will inject into all fitting the criteria if false, otherwise first found.
|
||||
var/first_match_only = TRUE
|
||||
///Will inject into atoms of this type.
|
||||
var/target_type
|
||||
///Will inject into atoms with this name.
|
||||
var/target_name
|
||||
/// This is the var tha will be set with the fetched icon. In case you want to set some secondary icon sheets like inhands and such.
|
||||
var/target_variable = "icon"
|
||||
/// This should return raw dmi in response to http get request. For example: "https://github.com/tgstation/SS13-sprites/raw/master/mob/medu.dmi?raw=true"
|
||||
var/icon_url
|
||||
|
||||
/obj/effect/mapping_helpers/custom_icon/LateInitialize()
|
||||
///TODO put this injector stuff under common root
|
||||
var/I = fetch_icon(icon_url)
|
||||
var/turf/target_turf = get_turf(src)
|
||||
var/matches_found = 0
|
||||
for(var/a in target_turf.GetAllContents())
|
||||
var/atom/atom_on_turf = a
|
||||
if(atom_on_turf == src)
|
||||
continue
|
||||
if(target_name && atom_on_turf.name != target_name)
|
||||
continue
|
||||
if(target_type && !istype(atom_on_turf,target_type))
|
||||
continue
|
||||
atom_on_turf.vars[target_variable] = I
|
||||
matches_found++
|
||||
if(first_match_only)
|
||||
qdel(src)
|
||||
return
|
||||
if(!matches_found)
|
||||
stack_trace("[src] found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name]")
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/custom_icon/proc/fetch_icon(url)
|
||||
var/static/icon_cache = list()
|
||||
var/static/query_in_progress = FALSE //We're using a single tmp file so keep it linear.
|
||||
if(query_in_progress)
|
||||
UNTIL(!query_in_progress)
|
||||
if(icon_cache[url])
|
||||
return icon_cache[url]
|
||||
log_asset("Custom Icon Helper fetching dmi from: [url]")
|
||||
var/datum/http_request/request = new()
|
||||
var/file_name = "tmp/custom_map_icon.dmi"
|
||||
request.prepare(RUSTG_HTTP_METHOD_GET, url , "", "", file_name)
|
||||
query_in_progress = TRUE
|
||||
request.begin_async()
|
||||
UNTIL(request.is_complete())
|
||||
var/datum/http_response/response = request.into_response()
|
||||
if(response.errored || response.status_code != 200)
|
||||
query_in_progress = FALSE
|
||||
CRASH("Failed to fetch mapped custom icon from url [url], code: [response.status_code], error: [response.error]")
|
||||
var/icon/I = new(file_name)
|
||||
icon_cache[url] = I
|
||||
query_in_progress = FALSE
|
||||
return I
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/baseturf_helper/proc/replace_baseturf(turf/thing)
|
||||
var/list/baseturf_cache = thing.baseturfs
|
||||
if(length(baseturf_cache))
|
||||
if(length(thing.baseturfs))
|
||||
var/list/baseturf_cache = thing.baseturfs.Copy()
|
||||
for(var/i in baseturf_cache)
|
||||
if(baseturf_to_replace[i])
|
||||
baseturf_cache -= i
|
||||
@@ -44,6 +44,8 @@
|
||||
else
|
||||
thing.PlaceOnBottom(null, baseturf)
|
||||
|
||||
|
||||
|
||||
/obj/effect/baseturf_helper/space
|
||||
name = "space baseturf editor"
|
||||
baseturf = /turf/open/space
|
||||
@@ -79,4 +81,3 @@
|
||||
/obj/effect/baseturf_helper/lava_land/surface
|
||||
name = "lavaland baseturf editor"
|
||||
baseturf = /turf/open/lava/smooth/lava_land_surface
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/obj/item/clothing/suit/hooded/explorer
|
||||
name = "explorer suit"
|
||||
desc = "An armoured suit for exploring harsh environments."
|
||||
icon_state = "explorer"
|
||||
item_state = "explorer"
|
||||
icon_state = "explorer-normal"
|
||||
item_state = "explorer-normal"
|
||||
var/suit_type = "normal"
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
cold_protection = CHEST|GROIN|LEGS|ARMS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
@@ -13,11 +14,15 @@
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe)
|
||||
resistance_flags = FIRE_PROOF
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC
|
||||
no_t = TRUE
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer
|
||||
name = "explorer hood"
|
||||
desc = "An armoured hood for exploring harsh environments."
|
||||
icon_state = "explorer"
|
||||
icon_state = "explorer-normal"
|
||||
item_state = "explorer-normal"
|
||||
var/suit_type = "normal"
|
||||
var/basestate = "normal"
|
||||
body_parts_covered = HEAD
|
||||
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
@@ -33,10 +38,40 @@
|
||||
/obj/item/clothing/suit/hooded/explorer/standard/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/suit/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
suit_type = "normal_goliath"
|
||||
if(amount == maxamount)
|
||||
suit_type = "normal_goliath_full"
|
||||
icon_state = "explorer-[suit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.wear_suit == src)
|
||||
wearer.update_inv_wear_suit()
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/standard/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
suit_type = "normal_goliath"
|
||||
if(amount == maxamount)
|
||||
suit_type = "normal_goliath_full"
|
||||
icon_state = "explorer-[suit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.head == src)
|
||||
wearer.update_inv_head()
|
||||
|
||||
/obj/item/clothing/mask/gas/explorer
|
||||
name = "explorer gas mask"
|
||||
|
||||
@@ -1058,3 +1058,58 @@
|
||||
/datum/sprite_accessory/hair/zone
|
||||
name = "Zone"
|
||||
icon_state = "hair_zone"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient
|
||||
icon = 'icons/mob/hair_gradients.dmi'
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/none
|
||||
name = "None"
|
||||
icon_state = "none"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/fadeup
|
||||
name = "Fade Up"
|
||||
icon_state = "fadeup"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/fadedown
|
||||
name = "Fade Down"
|
||||
icon_state = "fadedown"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/vertical_split
|
||||
name = "Vertical Split"
|
||||
icon_state = "vsplit"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/_split
|
||||
name = "Horizontal Split"
|
||||
icon_state = "bottomflat"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/reflected
|
||||
name = "Reflected"
|
||||
icon_state = "reflected_high"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/reflected_inverse
|
||||
name = "Reflected Inverse"
|
||||
icon_state = "reflected_inverse_high"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/wavy
|
||||
name = "Wavy"
|
||||
icon_state = "wavy"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/long_fade_up
|
||||
name = "Long Fade Up"
|
||||
icon_state = "long_fade_up"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/long_fade_down
|
||||
name = "Long Fade Down"
|
||||
icon_state = "long_fade_down"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/short_fade_up
|
||||
name = "Short Fade Up"
|
||||
icon_state = "short_fade_up"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/short_fade_down
|
||||
name = "Short Fade Down"
|
||||
icon_state = "short_fade_down"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/wavy_spike
|
||||
name = "Spiked Wavy"
|
||||
icon_state = "wavy_spiked"
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
|
||||
name = "Synthetic Lizard - Pecs Light"
|
||||
icon_state = "synthlizpecslight"
|
||||
covered_limbs = list("Chest" = MATRIX_GREEN_BLUE)
|
||||
covered_limbs = list("Chest" = MATRIX_GREEN_BLUE, "Left Arm" = MATRIX_BLUE, "Right Arm" = MATRIX_BLUE, "Left Leg" = MATRIX_GREEN, "Right Leg" = MATRIX_GREEN)
|
||||
|
||||
/datum/sprite_accessory/mam_body_markings/synthliz
|
||||
recommended_species = list("synthliz")
|
||||
icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
|
||||
name = "Synthetic Lizard - Plates"
|
||||
icon_state = "synthlizscutes"
|
||||
covered_limbs = list("Chest" = MATRIX_GREEN)
|
||||
covered_limbs = list("Chest" = MATRIX_GREEN, "Left Leg" = MATRIX_GREEN, "Right Leg" = MATRIX_GREEN)
|
||||
|
||||
//Synth tails
|
||||
/datum/sprite_accessory/tails/mam_tails/synthliz
|
||||
|
||||
@@ -542,6 +542,20 @@
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails/human/takahiro_kitsune
|
||||
name = "Takahiro Kitsune Tails" //takahiro had five tails i just wanted to follow the 'T' naming convention vs. tamamo and triple
|
||||
icon_state = "7sune"
|
||||
color_src = MATRIXED
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails_animated/human/takahiro_kitsune
|
||||
name = "Takahiro Kitsune Tails"
|
||||
icon_state = "7sune"
|
||||
color_src = MATRIXED
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails/human/tentacle
|
||||
name = "Tentacle"
|
||||
icon_state = "tentacle"
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
if (isturf(T))
|
||||
update_z(T.z)
|
||||
|
||||
update_icon(preferred_form)
|
||||
update_icon(null, preferred_form)
|
||||
updateghostimages()
|
||||
|
||||
@@ -176,7 +176,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
* Hair will always update its dir, so if your sprite has no dirs the haircut will go all over the place.
|
||||
* |- Ricotez
|
||||
*/
|
||||
/mob/dead/observer/update_icon(new_form)
|
||||
/mob/dead/observer/update_icon(updates=ALL, new_form=null)
|
||||
. = ..()
|
||||
if(client) //We update our preferences in case they changed right before update_icon was called.
|
||||
ghost_accs = client.prefs.ghost_accs
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
var/hair_color = "000"
|
||||
var/hair_style = "Bald"
|
||||
|
||||
///Colour used for the hair gradient.
|
||||
var/grad_color = "000"
|
||||
///Style used for the hair gradient.
|
||||
var/grad_style
|
||||
|
||||
//Facial hair colour and style
|
||||
var/facial_hair_color = "000"
|
||||
var/facial_hair_style = "Shaved"
|
||||
@@ -81,6 +86,8 @@
|
||||
|
||||
tooltips = TRUE
|
||||
|
||||
var/additional_language //the additional language this human can speak from their preference selection
|
||||
|
||||
/// Unarmed parry data for human
|
||||
/datum/block_parry_data/unarmed/human
|
||||
parry_respect_clickdelay = TRUE
|
||||
|
||||
@@ -52,6 +52,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/hair_color
|
||||
///The alpha used by the hair. 255 is completely solid, 0 is invisible.
|
||||
var/hair_alpha = 255
|
||||
///The gradient style used for the mob's hair.
|
||||
var/grad_style
|
||||
///The gradient color used to color the gradient.
|
||||
var/grad_color
|
||||
|
||||
///Does the species use skintones or not? As of now only used by humans.
|
||||
var/use_skintones = FALSE
|
||||
@@ -678,6 +682,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
if(!hair_hidden || dynamic_hair_suffix)
|
||||
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
||||
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
||||
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
|
||||
if(!(NOBLOOD in species_traits))
|
||||
hair_overlay.icon = 'icons/mob/human_parts.dmi'
|
||||
@@ -713,8 +718,21 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
hair_overlay.color = "#" + hair_color
|
||||
else
|
||||
hair_overlay.color = "#" + H.hair_color
|
||||
|
||||
//Gradients
|
||||
grad_style = H.grad_style
|
||||
grad_color = H.grad_color
|
||||
if(grad_style)
|
||||
var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
|
||||
var/icon/temp = icon(gradient.icon, gradient.icon_state)
|
||||
var/icon/temp_hair = icon(hair_file, hair_state)
|
||||
temp.Blend(temp_hair, ICON_ADD)
|
||||
gradient_overlay.icon = temp
|
||||
gradient_overlay.color = "#" + grad_color
|
||||
|
||||
else
|
||||
hair_overlay.color = forced_colour
|
||||
|
||||
hair_overlay.alpha = hair_alpha
|
||||
|
||||
if(OFFSET_HAIR in H.dna.species.offset_features)
|
||||
@@ -723,6 +741,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
if(hair_overlay.icon)
|
||||
standing += hair_overlay
|
||||
standing += gradient_overlay
|
||||
|
||||
if(standing.len)
|
||||
H.overlays_standing[HAIR_LAYER] = standing
|
||||
@@ -1980,7 +1999,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(CHECK_MOBILITY(target, MOBILITY_STAND))
|
||||
target.adjustStaminaLoss(5)
|
||||
else
|
||||
target.adjustStaminaLoss(target.getStaminaLoss() > 75? 5 : 75)
|
||||
target.adjustStaminaLoss(IS_STAMCRIT(target)? 2 : 10)
|
||||
|
||||
if(target.is_shove_knockdown_blocked())
|
||||
return
|
||||
|
||||
@@ -807,11 +807,11 @@
|
||||
|
||||
/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species)
|
||||
. = ..()
|
||||
C.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
C.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_NUDE)
|
||||
|
||||
/datum/species/golem/plastic/on_species_loss(mob/living/carbon/C)
|
||||
. = ..()
|
||||
C.RemoveElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
C.RemoveElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_NUDE)
|
||||
|
||||
/datum/species/golem/bronze
|
||||
name = "Bronze Golem"
|
||||
|
||||
@@ -103,13 +103,16 @@
|
||||
var/_method = override[thing]
|
||||
if(_method == ITEM_PARRY)
|
||||
using_item = thing
|
||||
tool = using_item
|
||||
method = ITEM_PARRY
|
||||
data = using_item.block_parry_data
|
||||
else if(_method == UNARMED_PARRY)
|
||||
method = UNARMED_PARRY
|
||||
tool = src
|
||||
data = thing
|
||||
if(!using_item && !method && length(other_items))
|
||||
using_item = other_items[1]
|
||||
tool = using_item
|
||||
method = ITEM_PARRY
|
||||
data = using_item.block_parry_data
|
||||
if(!method)
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
throwpower = I.throwforce
|
||||
var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
|
||||
var/list/block_return = list()
|
||||
var/total_damage = I.throwforce
|
||||
var/total_damage = AM.throwforce
|
||||
if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, block_return) & BLOCK_SUCCESS)
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE
|
||||
|
||||
@@ -322,6 +322,17 @@
|
||||
else
|
||||
to_chat(user, "Encryption Key ports not configured.")
|
||||
|
||||
/obj/item/paicard/emag_act(mob/user) // Emag to wipe the master DNA and supplemental directive
|
||||
. = ..()
|
||||
if(!pai)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You override [pai]'s directive system, clearing its master string and supplied directive.</span>")
|
||||
to_chat(pai, "<span class='danger'>Warning: System override detected, check directive sub-system for any changes.'</span>")
|
||||
log_game("[key_name(user)] emagged [key_name(pai)], wiping their master DNA and supplemental directive.")
|
||||
pai.master = null
|
||||
pai.master_dna = null
|
||||
pai.laws.supplied[1] = "None." // Sets supplemental directive to this
|
||||
|
||||
/mob/living/silicon/pai/proc/short_radio()
|
||||
if(radio_short_timerid)
|
||||
deltimer(radio_short_timerid)
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
|
||||
/**
|
||||
* Returns the thing in our active hand (whatever is in our active module-slot, in this case)
|
||||
*
|
||||
* Arguments
|
||||
* * get_gripper - If the active module is a gripper, should we return the gripper or the contained item? (if the gripper contains nothing, returns the gripper anyways)
|
||||
*/
|
||||
/mob/living/silicon/robot/get_active_held_item()
|
||||
/mob/living/silicon/robot/get_active_held_item(get_gripper = FALSE)
|
||||
var/item = module_active
|
||||
// snowflake handler for the gripper
|
||||
if(istype(item, /obj/item/weapon/gripper))
|
||||
var/obj/item/weapon/gripper/G = item
|
||||
if(istype(item, /obj/item/gripper) && !get_gripper)
|
||||
var/obj/item/gripper/G = item
|
||||
if(G.wrapped)
|
||||
if(G.wrapped.loc != G)
|
||||
G.wrapped = null
|
||||
@@ -284,9 +287,14 @@
|
||||
|
||||
/**
|
||||
* Unequips the active held item, if there is one.
|
||||
*
|
||||
* Will always consider dropping gripper contents first.
|
||||
*/
|
||||
/mob/living/silicon/robot/proc/uneq_active()
|
||||
if(module_active)
|
||||
var/obj/item/gripper/gripper = get_active_held_item(TRUE)
|
||||
if(istype(gripper) && gripper.drop_held())
|
||||
return
|
||||
unequip_module_from_slot(module_active, get_selected_module())
|
||||
|
||||
/**
|
||||
@@ -302,11 +310,12 @@
|
||||
* Checks if the item is currently in a slot.
|
||||
*
|
||||
* If the item is found in a slot, this returns TRUE. Otherwise, it returns FALSE
|
||||
* Modified to accept items inside of grippers, used for `code\modules\tgui\states\hands.dm:27`
|
||||
* Arguments
|
||||
* * item_module - the item being checked
|
||||
*/
|
||||
/mob/living/silicon/robot/proc/activated(obj/item/item_module)
|
||||
if(item_module in held_items)
|
||||
if(get_active_held_item() == item_module || (item_module in held_items))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@
|
||||
/obj/item/crowbar/cyborg,
|
||||
/obj/item/healthanalyzer,
|
||||
/obj/item/reagent_containers/borghypo,
|
||||
/obj/item/weapon/gripper/medical,
|
||||
/obj/item/gripper/medical,
|
||||
/obj/item/reagent_containers/dropper,
|
||||
/obj/item/reagent_containers/syringe,
|
||||
/obj/item/surgical_drapes,
|
||||
@@ -448,7 +448,7 @@
|
||||
/obj/item/analyzer,
|
||||
/obj/item/storage/part_replacer/cyborg,
|
||||
/obj/item/holosign_creator/combifan,
|
||||
/obj/item/weapon/gripper,
|
||||
/obj/item/gripper,
|
||||
/obj/item/lightreplacer/cyborg,
|
||||
/obj/item/geiger_counter/cyborg,
|
||||
/obj/item/assembly/signaler/cyborg,
|
||||
@@ -923,7 +923,7 @@
|
||||
/obj/item/gun/energy/kinetic_accelerator/cyborg,
|
||||
/obj/item/gun/energy/plasmacutter/cyborg,
|
||||
/obj/item/gps/cyborg,
|
||||
/obj/item/weapon/gripper/mining,
|
||||
/obj/item/gripper/mining,
|
||||
/obj/item/cyborg_clamp,
|
||||
/obj/item/stack/marker_beacon,
|
||||
/obj/item/destTagger,
|
||||
@@ -1075,7 +1075,7 @@
|
||||
/obj/item/multitool/cyborg,
|
||||
/obj/item/storage/part_replacer/cyborg,
|
||||
/obj/item/holosign_creator/atmos,
|
||||
/obj/item/weapon/gripper,
|
||||
/obj/item/gripper,
|
||||
/obj/item/lightreplacer/cyborg,
|
||||
/obj/item/stack/sheet/metal/cyborg,
|
||||
/obj/item/stack/sheet/glass/cyborg,
|
||||
|
||||
@@ -98,6 +98,18 @@
|
||||
qdel(bigcheese)
|
||||
evolve()
|
||||
return
|
||||
for(var/obj/item/trash/garbage in range(1, src))
|
||||
if(is_station_level(z))
|
||||
if(prob(2))
|
||||
qdel(garbage)
|
||||
evolve_plague()
|
||||
return
|
||||
for(var/obj/effect/decal/cleanable/blood/gibs/leftovers in range(1, src))
|
||||
if(is_station_level(z))
|
||||
if(prob(2))
|
||||
qdel(leftovers)
|
||||
evolve_plague()
|
||||
return
|
||||
|
||||
/**
|
||||
*Checks the mouse cap, if it's above the cap, doesn't spawn a mouse. If below, spawns a mouse and adds it to cheeserats.
|
||||
@@ -123,6 +135,17 @@
|
||||
mind.transfer_to(regalrat)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/mouse/proc/evolve_plague()
|
||||
return
|
||||
|
||||
/*
|
||||
var/mob/living/simple_animal/hostile/plaguerat = new /mob/living/simple_animal/hostile/plaguerat(loc)
|
||||
visible_message("<span class='warning'>[src] devours the food! He rots into something worse!</span>")
|
||||
if(mind)
|
||||
mind.transfer_to(plaguerat)
|
||||
qdel(src)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Mouse types
|
||||
*/
|
||||
@@ -169,3 +192,17 @@ GLOBAL_VAR(tom_existed)
|
||||
/obj/item/reagent_containers/food/snacks/deadmouse/on_grind()
|
||||
reagents.clear_reagents()
|
||||
|
||||
/mob/living/simple_animal/mouse/proc/miasma(datum/gas_mixture/environment, check_temp = FALSE)
|
||||
if(isturf(src.loc) && isopenturf(src.loc))
|
||||
var/turf/open/ST = src.loc
|
||||
var/miasma_moles = ST.air.get_moles(GAS_MIASMA)
|
||||
if(prob(5) && miasma_moles >= 5)
|
||||
evolve_plague()
|
||||
else if(miasma_moles >= 20)
|
||||
evolve_plague()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/mouse/handle_environment(datum/gas_mixture/environment)
|
||||
. = ..()
|
||||
if(is_station_level(z))
|
||||
miasma()
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
#define GREMLIN_VENT_CHANCE 1.75
|
||||
|
||||
//Gremlins
|
||||
//Small monsters that don't attack humans or other animals. Instead they mess with electronics, computers and machinery
|
||||
|
||||
//List of objects that gremlins can't tamper with (because nobody coded an interaction for it)
|
||||
//List starts out empty. Whenever a gremlin finds a machine that it couldn't tamper with, the machine's type is added here, and all machines of such type are ignored from then on (NOT SUBTYPES)
|
||||
GLOBAL_LIST(bad_gremlin_items)
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin
|
||||
name = "gremlin"
|
||||
desc = "This tiny creature finds great joy in discovering and using technology. Nothing excites it more than pushing random buttons on a computer to see what it might do."
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "gremlin"
|
||||
icon_living = "gremlin"
|
||||
icon_dead = "gremlin_dead"
|
||||
|
||||
var/in_vent = FALSE
|
||||
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
search_objects = 3 //Completely ignore mobs
|
||||
|
||||
//Tampering is handled by the 'npc_tamper()' obj proc
|
||||
wanted_objects = list(
|
||||
/obj/machinery,
|
||||
/obj/item/reagent_containers/food,
|
||||
/obj/structure/sink
|
||||
)
|
||||
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent
|
||||
|
||||
dextrous = TRUE
|
||||
possible_a_intents = list(INTENT_HELP, INTENT_GRAB, INTENT_DISARM, INTENT_HARM)
|
||||
faction = list("meme", "gremlin")
|
||||
speed = 0.5
|
||||
gold_core_spawnable = 2
|
||||
unique_name = TRUE
|
||||
|
||||
//Ensure gremlins don't attack other mobs
|
||||
melee_damage_upper = 0
|
||||
melee_damage_lower = 0
|
||||
attack_sound = null
|
||||
obj_damage = 0
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
|
||||
//List of objects that we don't even want to try to tamper with
|
||||
//Subtypes of these are calculated too
|
||||
var/list/unwanted_objects = list(/obj/machinery/atmospherics/pipe, /turf, /obj/structure) //ensure gremlins dont try to fuck with walls / normal pipes / glass / etc
|
||||
|
||||
var/min_next_vent = 0
|
||||
|
||||
//Amount of ticks spent pathing to the target. If it gets above a certain amount, assume that the target is unreachable and stop
|
||||
var/time_chasing_target = 0
|
||||
|
||||
//If you're going to make gremlins slower, increase this value - otherwise gremlins will abandon their targets too early
|
||||
var/max_time_chasing_target = 2
|
||||
|
||||
var/next_eat = 0
|
||||
|
||||
//Last 20 heard messages are remembered by gremlins, and will be used to generate messages for comms console tampering, etc...
|
||||
var/list/hear_memory = list()
|
||||
var/const/max_hear_memory = 20
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
access_card = new /obj/item/card/id(src)
|
||||
var/datum/job/captain/C = new /datum/job/captain
|
||||
access_card.access = C.get_access()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/AttackingTarget()
|
||||
var/is_hungry = world.time >= next_eat || prob(25)
|
||||
if(istype(target, /obj/item/reagent_containers/food) && is_hungry) //eat food if we're hungry or bored
|
||||
visible_message("<span class='danger'>[src] hungrily devours [target]!</span>")
|
||||
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
||||
qdel(target)
|
||||
LoseTarget()
|
||||
next_eat = world.time + rand(700, 3000) //anywhere from 70 seconds to 5 minutes until the gremlin is hungry again
|
||||
return
|
||||
if(istype(target, /obj))
|
||||
var/obj/M = target
|
||||
tamper(M)
|
||||
if(prob(50)) //50% chance to move to the next machine
|
||||
LoseTarget()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode)
|
||||
. = ..()
|
||||
if(message)
|
||||
hear_memory.Insert(1, raw_message)
|
||||
if(hear_memory.len > max_hear_memory)
|
||||
hear_memory.Cut(hear_memory.len)
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/generate_markov_input()
|
||||
var/result = ""
|
||||
|
||||
for(var/memory in hear_memory)
|
||||
result += memory + " "
|
||||
|
||||
return result
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/generate_markov_chain()
|
||||
return markov_chain(generate_markov_input(), rand(2,5), rand(100,700)) //The numbers are chosen arbitarily
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/tamper(obj/M)
|
||||
switch(M.npc_tamper_act(src))
|
||||
if(NPC_TAMPER_ACT_FORGET)
|
||||
visible_message(pick(
|
||||
"<span class='notice'>\The [src] plays around with \the [M], but finds it rather boring.</span>",
|
||||
"<span class='notice'>\The [src] tries to think of some more ways to screw \the [M] up, but fails miserably.</span>",
|
||||
"<span class='notice'>\The [src] decides to ignore \the [M], and starts looking for something more fun.</span>"))
|
||||
|
||||
LAZYADD(GLOB.bad_gremlin_items,M.type)
|
||||
return FALSE
|
||||
if(NPC_TAMPER_ACT_NOMSG)
|
||||
//Don't create a visible message
|
||||
return TRUE
|
||||
|
||||
else
|
||||
visible_message(pick(
|
||||
"<span class='danger'>\The [src]'s eyes light up as \he tampers with \the [M].</span>",
|
||||
"<span class='danger'>\The [src] twists some knobs around on \the [M] and bursts into laughter!</span>",
|
||||
"<span class='danger'>\The [src] presses a few buttons on \the [M] and giggles mischievously.</span>",
|
||||
"<span class='danger'>\The [src] rubs its hands devilishly and starts messing with \the [M].</span>",
|
||||
"<span class='danger'>\The [src] turns a small valve on \the [M].</span>"))
|
||||
|
||||
//Add a clue for detectives to find. The clue is only added if no such clue already existed on that machine
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/CanAttack(atom/new_target)
|
||||
if(LAZYFIND(GLOB.bad_gremlin_items,new_target.type))
|
||||
return FALSE
|
||||
if(is_type_in_list(new_target, unwanted_objects))
|
||||
return FALSE
|
||||
if(istype(new_target, /obj/machinery))
|
||||
var/obj/machinery/M = new_target
|
||||
if(M.stat) //Unpowered or broken
|
||||
return FALSE
|
||||
else if(istype(new_target, /obj/machinery/door/firedoor))
|
||||
var/obj/machinery/door/firedoor/F = new_target
|
||||
//Only tamper with firelocks that are closed, opening them!
|
||||
if(!F.density)
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/death(gibbed)
|
||||
walk(src,0)
|
||||
QDEL_NULL(access_card)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Life()
|
||||
. = ..()
|
||||
if(!health || stat == DEAD)
|
||||
return
|
||||
//Don't try to path to one target for too long. If it takes longer than a certain amount of time, assume it can't be reached and find a new one
|
||||
if(!client) //don't do this shit if there's a client, they're capable of ventcrawling manually
|
||||
if(in_vent)
|
||||
target = null
|
||||
if(entry_vent && get_dist(src, entry_vent) <= 1)
|
||||
var/list/vents = list()
|
||||
var/datum/pipeline/entry_vent_parent = entry_vent.parents[1]
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in entry_vent_parent.other_atmosmch)
|
||||
vents += temp_vent
|
||||
if(!vents.len)
|
||||
entry_vent = null
|
||||
in_vent = FALSE
|
||||
return
|
||||
exit_vent = pick(vents)
|
||||
visible_message("<span class='notice'>[src] crawls into the ventilation ducts!</span>")
|
||||
|
||||
loc = exit_vent
|
||||
var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
|
||||
addtimer(CALLBACK(src, .proc/exit_vents), travel_time) //come out at exit vent in 2 to 20 seconds
|
||||
|
||||
|
||||
if(world.time > min_next_vent && !entry_vent && !in_vent && prob(GREMLIN_VENT_CHANCE)) //small chance to go into a vent
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/v in view(7,src))
|
||||
if(!v.welded)
|
||||
entry_vent = v
|
||||
in_vent = TRUE
|
||||
walk_to(src, entry_vent)
|
||||
break
|
||||
if(!target)
|
||||
time_chasing_target = 0
|
||||
else
|
||||
if(++time_chasing_target > max_time_chasing_target)
|
||||
LoseTarget()
|
||||
time_chasing_target = 0
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/EscapeConfinement()
|
||||
if(istype(loc, /obj) && CanAttack(loc)) //If we're inside a machine, screw with it
|
||||
var/obj/M = loc
|
||||
tamper(M)
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/exit_vents()
|
||||
if(!exit_vent || exit_vent.welded)
|
||||
loc = entry_vent
|
||||
entry_vent = null
|
||||
return
|
||||
loc = exit_vent.loc
|
||||
entry_vent = null
|
||||
exit_vent = null
|
||||
in_vent = FALSE
|
||||
var/area/new_area = get_area(loc)
|
||||
message_admins("[src] came out at [new_area][ADMIN_JMP(loc)]!")
|
||||
if(new_area)
|
||||
new_area.Entered(src)
|
||||
visible_message("<span class='notice'>[src] climbs out of the ventilation ducts!</span>")
|
||||
min_next_vent = world.time + 900 //90 seconds between ventcrawls
|
||||
|
||||
//This allows player-controlled gremlins to tamper with machinery
|
||||
/mob/living/simple_animal/hostile/gremlin/UnarmedAttack(var/atom/A)
|
||||
if(istype(A, /obj/machinery) || istype(A, /obj/structure))
|
||||
tamper(A)
|
||||
if(istype(target, /obj/item/reagent_containers/food)) //eat food
|
||||
visible_message("<span class='danger'>[src] hungrily devours [target]!</span>", "<span class='danger'>You hungrily devour [target]!</span>")
|
||||
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
||||
qdel(target)
|
||||
LoseTarget()
|
||||
next_eat = world.time + rand(700, 3000) //anywhere from 70 seconds to 5 minutes until the gremlin is hungry again
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/IsAdvancedToolUser()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/divide()
|
||||
//Health is halved and then reduced by 2. A new gremlin is spawned with the same health as the parent
|
||||
//Need to have at least 6 health for this, otherwise resulting health would be less than 1
|
||||
if(health < 7.5)
|
||||
return
|
||||
|
||||
visible_message("<span class='notice'>\The [src] splits into two!</span>")
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = new /mob/living/simple_animal/hostile/gremlin(get_turf(src))
|
||||
|
||||
if(mind)
|
||||
mind.transfer_to(G)
|
||||
|
||||
health = round(health * 0.5) - 2
|
||||
maxHealth = health
|
||||
resize *= 0.9
|
||||
|
||||
G.health = health
|
||||
G.maxHealth = maxHealth
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/traitor
|
||||
health = 85
|
||||
maxHealth = 85
|
||||
gold_core_spawnable = 0
|
||||
@@ -0,0 +1,214 @@
|
||||
/obj/proc/npc_tamper_act(mob/living/L)
|
||||
return NPC_TAMPER_ACT_FORGET
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/passive_gate/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
target_pressure = rand(0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/pump/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
target_pressure = rand(0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/volume_pump/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
transfer_rate = rand(0, MAX_TRANSFER_RATE)
|
||||
investigate_log("was set to [transfer_rate] L/s by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/valve/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/space_heater/npc_tamper_act(mob/living/L)
|
||||
var/list/choose_modes = list("standby", "heat", "cool")
|
||||
if(prob(50))
|
||||
choose_modes -= mode
|
||||
mode = pick(choose_modes)
|
||||
else
|
||||
on = !on
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/shield_gen/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/firealarm/npc_tamper_act(mob/living/L)
|
||||
alarm()
|
||||
|
||||
/obj/machinery/airalarm/npc_tamper_act(mob/living/L)
|
||||
if(panel_open)
|
||||
wires.npc_tamper(L)
|
||||
else
|
||||
panel_open = !panel_open
|
||||
|
||||
/obj/machinery/ignition_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/flasher_button/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/crema_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/camera/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/npc_tamper_act(mob/living/L)
|
||||
if(prob(50))
|
||||
if(beaker)
|
||||
beaker.forceMove(loc)
|
||||
beaker = null
|
||||
else
|
||||
if(occupant)
|
||||
if(state_open)
|
||||
if (close_machine() == usr)
|
||||
on = TRUE
|
||||
else
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/door_control/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/door/airlock/npc_tamper_act(mob/living/L)
|
||||
//Open the firelocks as well, otherwise they block the way for our gremlin which isn't fun
|
||||
for(var/obj/machinery/door/firedoor/F in get_turf(src))
|
||||
if(F.density)
|
||||
F.npc_tamper_act(L)
|
||||
|
||||
if(prob(40)) //40% - mess with wires
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
else //60% - just open it
|
||||
open()
|
||||
|
||||
/obj/machinery/gibber/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/light_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/turretid/npc_tamper_act(mob/living/L)
|
||||
enabled = rand(0, 1)
|
||||
lethal = rand(0, 1)
|
||||
updateTurrets()
|
||||
|
||||
/obj/machinery/vending/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/shower/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
|
||||
/obj/machinery/deepfryer/npc_tamper_act(mob/living/L)
|
||||
//Deepfry a random nearby item
|
||||
var/list/pickable_items = list()
|
||||
|
||||
for(var/obj/item/I in range(1, L))
|
||||
pickable_items.Add(I)
|
||||
|
||||
if(!pickable_items.len)
|
||||
return
|
||||
|
||||
var/obj/item/I = pick(pickable_items)
|
||||
|
||||
attackby(I, L) //shove the item in, even if it can't be deepfried normally
|
||||
|
||||
/obj/machinery/power/apc/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/power/rad_collector/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/power/emitter/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/computer/communications/npc_tamper_act(mob/living/user)
|
||||
if(!authenticated)
|
||||
if(prob(20)) //20% chance to log in
|
||||
authenticated = TRUE
|
||||
|
||||
else //Already logged in
|
||||
if(prob(50)) //50% chance to log off
|
||||
authenticated = FALSE
|
||||
else if(istype(user, /mob/living/simple_animal/hostile/gremlin)) //make a hilarious public message
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = user
|
||||
var/result = G.generate_markov_chain()
|
||||
|
||||
if(result)
|
||||
if(prob(85))
|
||||
SScommunications.make_announcement(G, FALSE, result)
|
||||
var/turf/T = get_turf(G)
|
||||
log_say("[key_name(usr)] ([ADMIN_JMP(T)]) has made a captain announcement: [result]")
|
||||
message_admins("[key_name_admin(G)] has made a captain announcement.", 1)
|
||||
else
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_IDLE)
|
||||
SSshuttle.requestEvac(G, result)
|
||||
else if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
|
||||
SSshuttle.cancelEvac(G)
|
||||
|
||||
/obj/machinery/button/door/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/sleeper/npc_tamper_act(mob/living/L)
|
||||
if(prob(75))
|
||||
inject_chem(pick(available_chems))
|
||||
else
|
||||
if(state_open)
|
||||
close_machine()
|
||||
else
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/power/smes/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //mess with input
|
||||
input_level = rand(0, input_level_max)
|
||||
else //mess with output
|
||||
output_level = rand(0, output_level_max)
|
||||
|
||||
/obj/machinery/syndicatebomb/npc_tamper_act(mob/living/L) //suicide bomber gremlins
|
||||
if(!open_panel)
|
||||
open_panel = !open_panel
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/computer/bank_machine/npc_tamper_act(mob/living/L)
|
||||
siphoning = !siphoning
|
||||
|
||||
/obj/machinery/computer/slot_machine/npc_tamper_act(mob/living/L)
|
||||
spin(L)
|
||||
|
||||
/obj/structure/sink/npc_tamper_act(mob/living/L)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/gremlin))
|
||||
visible_message("<span class='danger'>\The [L] climbs into \the [src] and turns the faucet on!</span>")
|
||||
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = L
|
||||
G.divide()
|
||||
|
||||
return NPC_TAMPER_ACT_NOMSG
|
||||
@@ -0,0 +1,44 @@
|
||||
/datum/round_event_control/gremlin
|
||||
name = "Spawn Gremlins"
|
||||
typepath = /datum/round_event/gremlin
|
||||
weight = 15
|
||||
max_occurrences = 2
|
||||
earliest_start = 20 MINUTES
|
||||
min_players = 5
|
||||
|
||||
|
||||
|
||||
/datum/round_event/gremlin
|
||||
var/static/list/acceptable_spawns = list("xeno_spawn", "generic event spawn", "blobstart", "Assistant")
|
||||
|
||||
/datum/round_event/gremlin/announce()
|
||||
priority_announce("Bioscans indicate that some gremlins entered through the vents. Deal with them!", "Gremlin Alert", 'sound/announcer/classic/attention.ogg')
|
||||
|
||||
/datum/round_event/gremlin/start()
|
||||
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
for(var/obj/effect/landmark/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc) && !isspaceturf(L.loc))
|
||||
if(L.name in acceptable_spawns)
|
||||
spawn_locs += L.loc
|
||||
if(!spawn_locs.len) //If we can't find any gremlin spawns, try the xeno spawns
|
||||
for(var/obj/effect/landmark/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
switch(L.name)
|
||||
if("Assistant")
|
||||
spawn_locs += L.loc
|
||||
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
|
||||
return MAP_ERROR
|
||||
|
||||
var/gremlins_to_spawn = rand(2,5)
|
||||
var/list/gremlin_areas = list()
|
||||
for(var/i = 0, i <= gremlins_to_spawn, i++)
|
||||
var/spawnat = pick(spawn_locs)
|
||||
spawn_locs -= spawnat
|
||||
gremlin_areas += get_area(spawnat)
|
||||
new /mob/living/simple_animal/hostile/gremlin(spawnat)
|
||||
var/grems = gremlin_areas.Join(", ")
|
||||
message_admins("Gremlins have been spawned at the areas: [grems]")
|
||||
log_game("Gremlins have been spawned at the areas: [grems]")
|
||||
return SUCCESSFUL_SPAWN
|
||||
@@ -60,6 +60,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
var/magic_fluff_string = "<span class='holoparasite'>You draw the Coder, symbolizing bugs and errors. This shouldn't happen! Submit a bug report!</span>"
|
||||
var/tech_fluff_string = "<span class='holoparasite'>BOOT SEQUENCE COMPLETE. ERROR MODULE LOADED. THIS SHOULDN'T HAPPEN. Submit a bug report!</span>"
|
||||
var/carp_fluff_string = "<span class='holoparasite'>CARP CARP CARP SOME SORT OF HORRIFIC BUG BLAME THE CODERS CARP CARP CARP</span>"
|
||||
var/customized
|
||||
/// sigh, fine.
|
||||
var/datum/song/holoparasite/music_datum
|
||||
|
||||
@@ -142,11 +143,13 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
to_chat(src, "<span class='holoparasite'>You are capable of manifesting or recalling to your master with the buttons on your HUD. You will also find a button to communicate with [summoner.p_them()] privately there.</span>")
|
||||
to_chat(src, "<span class='holoparasite'>While personally invincible, you will die if [summoner.real_name] does, and any damage dealt to you will have a portion passed on to [summoner.p_them()] as you feed upon [summoner.p_them()] to sustain yourself.</span>")
|
||||
to_chat(src, playstyle_string)
|
||||
guardiancustomize()
|
||||
if(!customized)
|
||||
guardiancustomize()
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/proc/guardiancustomize()
|
||||
guardianrecolor()
|
||||
guardianrename()
|
||||
customized = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/proc/guardianrecolor()
|
||||
guardiancolor = input(src,"What would you like your color to be?","Choose Your Color","#ffffff") as color|null
|
||||
|
||||
@@ -528,21 +528,7 @@ Difficulty: Very Hard
|
||||
/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method)
|
||||
if(..())
|
||||
var/obj/item/projectile/P = new generated_projectile(get_turf(src))
|
||||
P.setDir(dir)
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
P.yo = 20
|
||||
P.xo = 0
|
||||
if(EAST)
|
||||
P.yo = 0
|
||||
P.xo = 20
|
||||
if(WEST)
|
||||
P.yo = 0
|
||||
P.xo = -20
|
||||
else
|
||||
P.yo = -20
|
||||
P.xo = 0
|
||||
P.fire()
|
||||
P.fire(angle2dir(dir))
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down.
|
||||
observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again."
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
#define RAT_VENT_CHANCE 1.75
|
||||
GLOBAL_LIST_EMPTY(plague_rats)
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat
|
||||
name = "plague rat"
|
||||
desc = "A large decaying rat. It spreads its filth and emits a putrid odor to create more of its kind."
|
||||
icon_state = "plaguerat"
|
||||
icon_living = "plaguerat"
|
||||
icon_dead = "plaguerat_dead"
|
||||
speak = list("Skree!","SKREEE!","Squeak?")
|
||||
speak_emote = list("squeaks")
|
||||
emote_hear = list("Hisses.")
|
||||
emote_see = list("runs in a circle.", "stands on its hind legs.")
|
||||
gender = NEUTER
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
see_in_dark = 6
|
||||
obj_damage = 10
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1)
|
||||
response_help_continuous = "glares at"
|
||||
response_help_simple = "glare at"
|
||||
response_disarm_continuous = "skoffs at"
|
||||
response_disarm_simple = "skoff at"
|
||||
response_harm_continuous = "slashes"
|
||||
response_harm_simple = "slash"
|
||||
melee_damage_lower = 6
|
||||
melee_damage_upper = 8
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
faction = list("rat")
|
||||
density = FALSE
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
var/datum/action/cooldown/scavenge
|
||||
var/last_spawn_time = 0
|
||||
var/in_vent = FALSE
|
||||
var/min_next_vent = 0
|
||||
var/obj/machinery/atmospherics/components/unary/entry_vent
|
||||
var/obj/machinery/atmospherics/components/unary/exit_vent
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/Initialize()
|
||||
. = ..()
|
||||
GLOB.plague_rats += src
|
||||
AddComponent(/datum/component/swarming)
|
||||
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
scavenge = new /datum/action/cooldown/scavenge
|
||||
scavenge.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/Destroy()
|
||||
GLOB.plague_rats -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
//Don't try to path to one target for too long. If it takes longer than a certain amount of time, assume it can't be reached and find a new one
|
||||
//Literally only here to prevent farming and that's it.
|
||||
if(!client) //don't do this shit if there's a client, they're capable of ventcrawling manually
|
||||
if(in_vent)
|
||||
target = null
|
||||
if(entry_vent && get_dist(src, entry_vent) <= 1)
|
||||
var/list/vents = list()
|
||||
var/datum/pipeline/entry_vent_parent = entry_vent.parents[1]
|
||||
for(var/obj/machinery/atmospherics/components/unary/temp_vent in entry_vent_parent.other_atmosmch)
|
||||
vents += temp_vent
|
||||
if(!vents.len)
|
||||
entry_vent = null
|
||||
in_vent = FALSE
|
||||
return
|
||||
exit_vent = pick(vents)
|
||||
visible_message("<span class='notice'>[src] crawls into the ventilation ducts!</span>")
|
||||
|
||||
loc = exit_vent
|
||||
var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
|
||||
addtimer(CALLBACK(src, .proc/exit_vents), travel_time) //come out at exit vent in 2 to 20 seconds
|
||||
|
||||
|
||||
if(world.time > min_next_vent && !entry_vent && !in_vent && prob(RAT_VENT_CHANCE)) //small chance to go into a vent
|
||||
for(var/obj/machinery/atmospherics/components/unary/v in view(7,src))
|
||||
if(!v.welded)
|
||||
entry_vent = v
|
||||
in_vent = TRUE
|
||||
walk_to(src, entry_vent)
|
||||
break
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/BiologicalLife(seconds, times_fired)
|
||||
if(!(. = ..()))
|
||||
return
|
||||
if(isopenturf(loc))
|
||||
var/turf/open/T = src.loc
|
||||
var/datum/gas_mixture/stank = new
|
||||
var/miasma_moles = T.air.get_moles(GAS_MIASMA)
|
||||
stank.set_moles(GAS_MIASMA,5)
|
||||
stank.set_temperature(BODYTEMP_NORMAL)
|
||||
if(T.air)
|
||||
if(miasma_moles < 200)
|
||||
T.assume_air(stank)
|
||||
T.air_update_turf()
|
||||
|
||||
if(prob(40))
|
||||
scavenge.Trigger()
|
||||
if(prob(50))
|
||||
var/turf/open/floor/F = get_turf(src)
|
||||
if(istype(F) && !F.intact)
|
||||
var/obj/structure/cable/C = locate() in F
|
||||
if(C && C.avail())
|
||||
visible_message("<span class='warning'>[src] chews through the [C]. It looks unharmed!</span>")
|
||||
playsound(src, 'sound/effects/sparks2.ogg', 100, TRUE)
|
||||
C.deconstruct()
|
||||
for(var/obj/O in range(1,src))
|
||||
if((world.time - last_spawn_time) > 10 SECONDS && istype(O, /obj/item/trash) || istype(O, /obj/effect/decal/cleanable/blood/gibs))
|
||||
qdel(O)
|
||||
be_fruitful()
|
||||
last_spawn_time = world.time
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/CanAttack(atom/the_target)
|
||||
if(istype(the_target,/mob/living/simple_animal))
|
||||
var/mob/living/A = the_target
|
||||
if(istype(the_target, /mob/living/simple_animal/hostile/plaguerat) && A.stat == CONSCIOUS)
|
||||
var/mob/living/simple_animal/hostile/plaguerat/R = the_target
|
||||
if(R.faction_check_mob(src, TRUE))
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/**
|
||||
*Checks the mouse cap, if it's above the cap, doesn't spawn a mouse. If below, spawns a mouse and adds it to cheeserats.
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/proc/be_fruitful()
|
||||
var/cap = 10
|
||||
if(LAZYLEN(GLOB.plague_rats) >= cap)
|
||||
visible_message("<span class='warning'>[src] gnaws into its food, [cap] rats are now on the station!</span>")
|
||||
return
|
||||
var/mob/living/newmouse = new /mob/living/simple_animal/hostile/plaguerat(loc)
|
||||
visible_message("<span class='notice'>[src] gnaws into its food, attracting another rat!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/proc/exit_vents()
|
||||
if(!exit_vent || exit_vent.welded)
|
||||
loc = entry_vent
|
||||
entry_vent = null
|
||||
return
|
||||
loc = exit_vent.loc
|
||||
entry_vent = null
|
||||
exit_vent = null
|
||||
in_vent = FALSE
|
||||
var/area/new_area = get_area(loc)
|
||||
message_admins("[src] came out at [new_area][ADMIN_JMP(loc)]!")
|
||||
if(new_area)
|
||||
new_area.Entered(src)
|
||||
visible_message("<span class='notice'>[src] climbs out of the ventilation ducts!</span>")
|
||||
min_next_vent = world.time + 900 //90 seconds between ventcrawls
|
||||
|
||||
/**
|
||||
*Creates a chance to spawn more trash or gibs to repopulate. Otherwise, spawns a corpse or dirt.
|
||||
*/
|
||||
|
||||
/datum/action/cooldown/scavenge
|
||||
name = "Scavenge"
|
||||
desc = "Spread the plague, scavenge for trash and fresh meat to reproduce."
|
||||
icon_icon = 'icons/mob/actions/actions_animal.dmi'
|
||||
background_icon_state = "bg_clock"
|
||||
button_icon_state = "coffer"
|
||||
cooldown_time = 50
|
||||
|
||||
/datum/action/cooldown/scavenge/Trigger()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/turf/T = get_turf(owner)
|
||||
var/loot = rand(1,100)
|
||||
switch(loot)
|
||||
if(1 to 3)
|
||||
var/pickedtrash = pick(GLOB.ratking_trash)
|
||||
to_chat(owner, "<span class='notice'>Excellent, you find more trash to spread your filth!</span>")
|
||||
new pickedtrash(T)
|
||||
if(4 to 6)
|
||||
to_chat(owner, "<span class='notice'>You find blood and gibs to feed your young!</span>")
|
||||
new /obj/effect/decal/cleanable/blood/gibs(T)
|
||||
if(!locate(/obj/effect/decal/cleanable/blood) in T)
|
||||
new /obj/effect/decal/cleanable/blood/(T)
|
||||
if(7 to 100)
|
||||
to_chat(owner, "<span class='notice'>Drat. Nothing.</span>")
|
||||
StartCooldown()
|
||||
@@ -109,7 +109,7 @@
|
||||
continue
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
visible_message("<span class='danger'>[src] vomits up [consumed_mob]!</span>")
|
||||
consumed_mob.forceMove(loc)
|
||||
consumed_mob.forceMove(get_turf(src))
|
||||
consumed_mob.Paralyze(50)
|
||||
if((rifts_charged == 3 || (SSshuttle.emergency.mode == SHUTTLE_DOCKED && rifts_charged > 0)) && !objective_complete)
|
||||
victory()
|
||||
@@ -123,6 +123,7 @@
|
||||
to_chat(src, "<span class='boldwarning'>You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!</span>")
|
||||
destroy_rifts()
|
||||
playsound(src, 'sound/magic/demon_dies.ogg', 100, TRUE)
|
||||
empty_contents()
|
||||
QDEL_NULL(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/AttackingTarget()
|
||||
@@ -351,7 +352,7 @@
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/empty_contents()
|
||||
for(var/atom/movable/AM in src)
|
||||
AM.forceMove(loc)
|
||||
AM.forceMove(get_turf(src))
|
||||
if(prob(90))
|
||||
step(AM, pick(GLOB.alldirs))
|
||||
|
||||
@@ -529,7 +530,7 @@
|
||||
/obj/structure/carp_rift
|
||||
name = "carp rift"
|
||||
desc = "A rift akin to the ones space carp use to travel long distances."
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
max_integrity = 300
|
||||
icon = 'icons/obj/carp_rift.dmi'
|
||||
icon_state = "carp_rift_carpspawn"
|
||||
@@ -636,7 +637,7 @@
|
||||
icon_state = "carp_rift_charged"
|
||||
light_color = LIGHT_COLOR_YELLOW
|
||||
update_light()
|
||||
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
dragon.rifts_charged += 1
|
||||
if(dragon.rifts_charged != 3 && !dragon.objective_complete)
|
||||
|
||||
@@ -238,6 +238,7 @@
|
||||
|
||||
if(istype(W))
|
||||
if(equip_to_slot_if_possible(W, slot, FALSE, FALSE, FALSE, FALSE, TRUE))
|
||||
W.apply_outline()
|
||||
return TRUE
|
||||
|
||||
if(!W)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
///Are you actually placing orders with it?
|
||||
var/requestonly = TRUE
|
||||
///Can the tablet see or buy illegal stuff?
|
||||
var/contraband = FALSE
|
||||
var/contraband_view = FALSE
|
||||
///Is it being bought from a personal account, or is it being done via a budget/cargo?
|
||||
var/self_paid = FALSE
|
||||
///Can this console approve purchase requests?
|
||||
@@ -27,10 +27,15 @@
|
||||
/datum/computer_file/program/budgetorders/proc/get_export_categories()
|
||||
. = EXPORT_CARGO
|
||||
|
||||
/datum/computer_file/program/budgetorders/run_emag()
|
||||
if(!contraband_view)
|
||||
contraband_view = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband)
|
||||
if(issilicon(user)) //Borgs can't buy things.
|
||||
return FALSE
|
||||
if(computer.obj_flags & EMAGGED)
|
||||
if((computer.obj_flags & EMAGGED) || contraband_view)
|
||||
return TRUE
|
||||
else if(contraband) //Hide contrband when non-emagged.
|
||||
return FALSE
|
||||
@@ -80,14 +85,14 @@
|
||||
data["supplies"] = list()
|
||||
for(var/pack in SSshuttle.supply_packs)
|
||||
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
|
||||
if(!is_visible_pack(usr, P.access , null, P.contraband) || P.hidden)
|
||||
if(!is_visible_pack(usr, P.access , null, P.contraband))
|
||||
continue
|
||||
if(!data["supplies"][P.group])
|
||||
data["supplies"][P.group] = list(
|
||||
"name" = P.group,
|
||||
"packs" = list()
|
||||
)
|
||||
if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly))
|
||||
if(((P.hidden || P.contraband) && !contraband_view) || (P.special && !P.special_enabled) || P.DropPodOnly)
|
||||
continue
|
||||
data["supplies"][P.group]["packs"] += list(list(
|
||||
"name" = P.name,
|
||||
@@ -179,7 +184,7 @@
|
||||
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
|
||||
if(!istype(pack))
|
||||
return
|
||||
if((pack.hidden && (pack.contraband && !contraband) || pack.DropPodOnly))
|
||||
if(((pack.hidden || pack.contraband) && !contraband_view) || pack.DropPodOnly)
|
||||
return
|
||||
|
||||
var/name = "*None Provided*"
|
||||
|
||||
@@ -32,7 +32,7 @@ Key procs
|
||||
/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
|
||||
var/id
|
||||
|
||||
/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
|
||||
/// Determines order. Lower priorities are applied first.
|
||||
var/priority = 0
|
||||
var/flags = NONE
|
||||
|
||||
@@ -41,9 +41,9 @@ Key procs
|
||||
/// Next two variables depend on this: Should we do advanced calculations?
|
||||
var/complex_calculation = FALSE
|
||||
/// Absolute max tiles we can boost to
|
||||
var/absolute_max_tiles_per_second
|
||||
var/absolute_max_tiles_per_second = INFINITY
|
||||
/// Max tiles per second we can boost
|
||||
var/max_tiles_per_second_boost
|
||||
var/max_tiles_per_second_boost = INFINITY
|
||||
|
||||
/// Movetypes this applies to
|
||||
var/movetypes = ALL
|
||||
@@ -54,6 +54,8 @@ Key procs
|
||||
/// Other modification datums this conflicts with.
|
||||
var/conflicts_with
|
||||
|
||||
|
||||
|
||||
/datum/movespeed_modifier/New()
|
||||
. = ..()
|
||||
if(!id)
|
||||
@@ -66,8 +68,10 @@ Key procs
|
||||
if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet.
|
||||
return existing + multiplicative_slowdown
|
||||
var/current_tiles = 10 / max(existing, world.tick_lag)
|
||||
var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second))
|
||||
return max(minimum_speed, existing + multiplicative_slowdown)
|
||||
// multiplicative_slowdown is negative due to our first check
|
||||
var/max_buff_to = max(existing + multiplicative_slowdown, 10 / absolute_max_tiles_per_second, 10 / (current_tiles + max_tiles_per_second_boost))
|
||||
// never slow the user
|
||||
return min(existing, max_buff_to)
|
||||
|
||||
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/datum/movespeed_modifier/reagent/ephedrine
|
||||
// strong painkiller effect that caps out at slightly above runspeed
|
||||
multiplicative_slowdown = -1.5
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
// extremely strong painkiller effect: allows user to run at old sprint speeds but not over by cancelling out slowdowns.
|
||||
// however, will not make user go faster than that
|
||||
multiplicative_slowdown = -4
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 8
|
||||
absolute_max_tiles_per_second = 10
|
||||
|
||||
/datum/movespeed_modifier/reagent/methamphetamine
|
||||
// very strong painkiller effect that caps out at slightly above runspeed
|
||||
multiplicative_slowdown = -2.5
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7.5
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
/datum/movespeed_modifier/status_effect/tased
|
||||
multiplicative_slowdown = 1.5
|
||||
priority = 50
|
||||
priority = 1500
|
||||
|
||||
/datum/movespeed_modifier/status_effect/domain
|
||||
multiplicative_slowdown = 3
|
||||
|
||||
/datum/movespeed_modifier/status_effect/tased/no_combat_mode
|
||||
multiplicative_slowdown = 8
|
||||
priority = 100
|
||||
priority = 1500
|
||||
|
||||
/datum/movespeed_modifier/status_effect/electrostaff
|
||||
multiplicative_slowdown = 1
|
||||
@@ -55,7 +55,7 @@
|
||||
/datum/movespeed_modifier/status_effect/slime/light_pink
|
||||
// decently good painkiller + speedup effect
|
||||
blacklisted_movetypes = FLYING | FLOATING
|
||||
priority = -150 // someday we really need to make these defines lmao
|
||||
priority = 500 // someday we really need to make these defines lmao
|
||||
multiplicative_slowdown = -2
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
|
||||
circuit = /obj/item/circuitboard/machine/hydroponics/automagic
|
||||
|
||||
|
||||
/obj/machinery/hydroponics/constructable/automagic/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, /obj/item/reagent_containers))
|
||||
return FALSE //avoid fucky wuckies
|
||||
..()
|
||||
|
||||
/obj/machinery/hydroponics/constructable/automagic/default_unfasten_wrench(mob/user, obj/item/I, time = 20)
|
||||
. = ..()
|
||||
if(. == SUCCESSFUL_UNFASTEN)
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
return 0
|
||||
if(charge < amount)
|
||||
return 0
|
||||
charge = (charge - amount)
|
||||
charge -= amount
|
||||
if(!istype(loc, /obj/machinery/power/apc))
|
||||
SSblackbox.record_feedback("tally", "cell_used", 1, type)
|
||||
return 1
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
#define DAMAGE_INCREASE_MULTIPLIER 0.25
|
||||
|
||||
|
||||
#define THERMAL_RELEASE_MODIFIER 5 //Higher == less heat released during reaction, not to be confused with the above values
|
||||
#define THERMAL_RELEASE_MODIFIER 350 //Higher == more heat released during reaction, not to be confused with the above values
|
||||
#define THERMAL_RELEASE_CAP_MODIFIER 250 //Higher == lower cap on how much heat can be released per tick--currently 1.3x old value
|
||||
#define PLASMA_RELEASE_MODIFIER 750 //Higher == less plasma released by reaction
|
||||
#define OXYGEN_RELEASE_MODIFIER 325 //Higher == less oxygen released at high temperature/power
|
||||
|
||||
@@ -500,7 +501,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling + clamp(powerloss_inhibition_gas - powerloss_dynamic_scaling, -0.02, 0.02), 0, 1)
|
||||
else
|
||||
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling - 0.05, 0, 1)
|
||||
//Ranges from 0 to 1(1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
|
||||
//Ranges from 0 to 1 (1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
|
||||
//0 means full inhibition, 1 means no inhibition
|
||||
//We take the mol count, and scale it to be our inhibitor
|
||||
powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD, 1, 1.5)), 0, 1)
|
||||
|
||||
@@ -523,9 +525,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
temp_factor = 30
|
||||
icon_state = base_icon_state
|
||||
|
||||
var/effective_temperature = min(removed.return_temperature(), 2500 * dynamic_heat_modifier)
|
||||
|
||||
//if there is more pluox and n2 then anything else, we receive no power increase from heat
|
||||
if(power_changes)
|
||||
power = max((removed.return_temperature() * temp_factor / T0C) * gasmix_power_ratio + power, 0)
|
||||
power = max((effective_temperature * temp_factor / T0C) * gasmix_power_ratio + power, 0)
|
||||
|
||||
if(prob(50))
|
||||
//(1 + (tritRad + pluoxDampen * bzDampen * o2Rad * plasmaRad / (10 - bzrads))) * freonbonus
|
||||
@@ -536,15 +540,17 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
//Power * 0.55 * a value between 1 and 0.8
|
||||
var/device_energy = power * REACTION_POWER_MODIFIER
|
||||
|
||||
removed.set_temperature(removed.return_temperature() + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER))
|
||||
//We don't want our output to be too hot
|
||||
removed.set_temperature(max(0, min(removed.return_temperature(), 2500 * dynamic_heat_modifier)))
|
||||
|
||||
var/max_temp_increase = effective_temperature + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_CAP_MODIFIER)
|
||||
//Calculate how much gas to release
|
||||
//Varies based on power and gas content
|
||||
removed.adjust_moles(GAS_PLASMA, max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0))
|
||||
//Varies based on power, gas content, and heat
|
||||
removed.adjust_moles(GAS_O2, max(((device_energy + removed.return_temperature() * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0))
|
||||
removed.adjust_moles(GAS_O2, max(((device_energy + effective_temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0))
|
||||
|
||||
if(removed.return_temperature() < max_temp_increase)
|
||||
removed.adjust_heat(device_energy * dynamic_heat_modifier * THERMAL_RELEASE_MODIFIER)
|
||||
removed.set_temperature(min(removed.return_temperature(), max_temp_increase))
|
||||
|
||||
|
||||
if(produces_gas)
|
||||
env.merge(removed)
|
||||
@@ -593,13 +599,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE
|
||||
//Uncaps the zap damage, it's maxed by the input power
|
||||
//Objects take damage now
|
||||
flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
|
||||
flags |= (ZAP_MOB_DAMAGE)
|
||||
zap_count = 3
|
||||
if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
|
||||
zap_icon = OVER_9000_ZAP_ICON_STATE
|
||||
//It'll stun more now, and damage will hit harder, gloves are no garentee.
|
||||
//Machines go boom
|
||||
flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
|
||||
flags |= (ZAP_MOB_STUN | ZAP_MOB_DAMAGE)
|
||||
zap_count = 4
|
||||
//Now we deal with damage shit
|
||||
if (damage > damage_penalty_point && prob(20))
|
||||
@@ -1052,9 +1058,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
if(zapdir)
|
||||
. = zapdir
|
||||
|
||||
//Going boom should be rareish
|
||||
if(prob(80))
|
||||
zap_flags &= ~ZAP_MACHINE_EXPLOSIVE
|
||||
if(target_type == COIL)
|
||||
//In the best situation we can expect this to grow up to 2120kw before a delam/IT'S GONE TOO FAR FRED SHUT IT DOWN
|
||||
//The formula for power gen is zap_str * zap_mod / 2 * capacitor rating, between 1 and 4
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
cell.use(round(cell.charge * severity/100))
|
||||
chambered = null //we empty the chamber
|
||||
recharge_newshot() //and try to charge a new shot
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/gun/energy/get_cell()
|
||||
return cell
|
||||
@@ -61,7 +61,7 @@
|
||||
recharge_newshot(TRUE)
|
||||
if(selfcharge)
|
||||
START_PROCESSING(SSobj, src)
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/gun/energy/ComponentInitialize()
|
||||
. = ..()
|
||||
@@ -74,7 +74,7 @@
|
||||
/obj/item/gun/energy/handle_atom_del(atom/A)
|
||||
if(A == cell)
|
||||
cell = null
|
||||
update_icon()
|
||||
update_appearance()
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/examine(mob/user)
|
||||
@@ -100,7 +100,7 @@
|
||||
cell.give(100)
|
||||
if(!chambered) //if empty chamber we try to charge a new shot
|
||||
recharge_newshot(TRUE)
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
// ATTACK SELF IGNORING PARENT RETURN VALUE
|
||||
/obj/item/gun/energy/attack_self(mob/living/user)
|
||||
@@ -174,7 +174,7 @@
|
||||
if(user_for_feedback)
|
||||
to_chat(user_for_feedback, "<span class='notice'>[src] is now set to [C.select_name || C].</span>")
|
||||
post_set_firemode()
|
||||
update_icon(TRUE)
|
||||
update_appearance()
|
||||
|
||||
/obj/item/gun/energy/proc/post_set_firemode(recharge_newshot = TRUE)
|
||||
if(recharge_newshot)
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
|
||||
..(target, blocked, TRUE)
|
||||
for(var/datum/reagent/medicine/R in reagents.reagent_list) //OD prevention time!
|
||||
if(R.type in GLOB.blacklisted_medchems)
|
||||
continue
|
||||
if(M.reagents.has_reagent(R.type))
|
||||
if(R.overdose_threshold == 0 || emptrig == TRUE) //Is there a possible OD?
|
||||
M.reagents.add_reagent(R.type, R.volume)
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
var/fermiIsReacting = FALSE //that prevents multiple reactions from occurring (i.e. add_reagent calls to process_reactions(), this stops any extra reactions.)
|
||||
var/fermiReactID //instance of the chem reaction used during a fermireaction, kept here so it's cache isn't lost between loops/procs.
|
||||
var/value_multiplier = DEFAULT_REAGENTS_VALUE //used for cargo reagents selling.
|
||||
var/force_alt_taste = FALSE
|
||||
|
||||
/datum/reagents/New(maximum=100, new_flags = NONE, new_value = DEFAULT_REAGENTS_VALUE)
|
||||
maximum_volume = maximum
|
||||
@@ -1120,47 +1121,54 @@
|
||||
. = locate(type) in cached_reagents
|
||||
|
||||
/datum/reagents/proc/generate_taste_message(minimum_percent=15)
|
||||
// the lower the minimum percent, the more sensitive the message is.
|
||||
var/list/out = list()
|
||||
var/list/tastes = list() //descriptor = strength
|
||||
if(minimum_percent <= 100)
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
if(!R.taste_mult)
|
||||
continue
|
||||
|
||||
if(istype(R, /datum/reagent/consumable/nutriment))
|
||||
var/list/taste_data = R.data
|
||||
for(var/taste in taste_data)
|
||||
var/ratio = taste_data[taste]
|
||||
var/amount = ratio * R.taste_mult * R.volume
|
||||
if(taste in tastes)
|
||||
tastes[taste] += amount
|
||||
else
|
||||
tastes[taste] = amount
|
||||
else
|
||||
var/taste_desc = R.taste_description
|
||||
var/taste_amount = R.volume * R.taste_mult
|
||||
if(taste_desc in tastes)
|
||||
tastes[taste_desc] += taste_amount
|
||||
else
|
||||
tastes[taste_desc] = taste_amount
|
||||
//deal with percentages
|
||||
// TODO it would be great if we could sort these from strong to weak
|
||||
var/total_taste = counterlist_sum(tastes)
|
||||
if(total_taste > 0)
|
||||
for(var/taste_desc in tastes)
|
||||
var/percent = tastes[taste_desc]/total_taste * 100
|
||||
if(percent < minimum_percent)
|
||||
if(!force_alt_taste)
|
||||
// the lower the minimum percent, the more sensitive the message is.
|
||||
var/list/tastes = list() //descriptor = strength
|
||||
if(minimum_percent <= 100)
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
if(!R.taste_mult)
|
||||
continue
|
||||
var/intensity_desc = "a hint of"
|
||||
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
|
||||
intensity_desc = ""
|
||||
else if(percent > minimum_percent * 3)
|
||||
intensity_desc = "the strong flavor of"
|
||||
if(intensity_desc != "")
|
||||
out += "[intensity_desc] [taste_desc]"
|
||||
|
||||
if(istype(R, /datum/reagent/consumable/nutriment))
|
||||
var/list/taste_data = R.data
|
||||
for(var/taste in taste_data)
|
||||
var/ratio = taste_data[taste]
|
||||
var/amount = ratio * R.taste_mult * R.volume
|
||||
if(taste in tastes)
|
||||
tastes[taste] += amount
|
||||
else
|
||||
tastes[taste] = amount
|
||||
else
|
||||
out += "[taste_desc]"
|
||||
var/taste_desc = R.taste_description
|
||||
var/taste_amount = R.volume * R.taste_mult
|
||||
if(taste_desc in tastes)
|
||||
tastes[taste_desc] += taste_amount
|
||||
else
|
||||
tastes[taste_desc] = taste_amount
|
||||
//deal with percentages
|
||||
// TODO it would be great if we could sort these from strong to weak
|
||||
var/total_taste = counterlist_sum(tastes)
|
||||
if(total_taste > 0)
|
||||
for(var/taste_desc in tastes)
|
||||
var/percent = tastes[taste_desc]/total_taste * 100
|
||||
if(percent < minimum_percent)
|
||||
continue
|
||||
var/intensity_desc = "a hint of"
|
||||
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
|
||||
intensity_desc = ""
|
||||
else if(percent > minimum_percent * 3)
|
||||
intensity_desc = "the strong flavor of"
|
||||
if(intensity_desc != "")
|
||||
out += "[intensity_desc] [taste_desc]"
|
||||
else
|
||||
out += "[taste_desc]"
|
||||
|
||||
else
|
||||
// alternate taste is to force the taste of the atom if its a food item
|
||||
if(my_atom && isfood(my_atom))
|
||||
var/obj/item/reagent_containers/food/snacks/F = my_atom
|
||||
out = F.tastes
|
||||
|
||||
return english_list(out, "something indescribable")
|
||||
|
||||
|
||||
@@ -554,6 +554,13 @@
|
||||
color = "#302000" // rgb: 48, 32, 0
|
||||
taste_description = "wet and cheap noodles"
|
||||
|
||||
/datum/reagent/consumable/nutraslop
|
||||
name = "Nutraslop"
|
||||
description = "Mixture of leftover prison foods served on previous days."
|
||||
nutriment_factor = 5 * REAGENTS_METABOLISM
|
||||
color = "#3E4A00" // rgb: 62, 74, 0
|
||||
taste_description = "your imprisonment"
|
||||
|
||||
/datum/reagent/consumable/hot_ramen/on_mob_life(mob/living/carbon/M)
|
||||
M.adjust_bodytemperature(10 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL)
|
||||
..()
|
||||
|
||||
@@ -1451,7 +1451,7 @@
|
||||
/datum/reagent/medicine/changelingadrenaline/on_mob_life(mob/living/carbon/metabolizer, delta_time, times_fired)
|
||||
..()
|
||||
metabolizer.AdjustAllImmobility(-20 * REM * delta_time)
|
||||
metabolizer.adjustStaminaLoss(-10 * REM * delta_time, 0)
|
||||
metabolizer.adjustStaminaLoss(-30 * REM * delta_time, 0)
|
||||
metabolizer.Jitter(10 * REM * delta_time)
|
||||
metabolizer.Dizzy(10 * REM * delta_time)
|
||||
return TRUE
|
||||
@@ -1477,7 +1477,7 @@
|
||||
|
||||
/datum/reagent/medicine/changelinghaste
|
||||
name = "Changeling Haste"
|
||||
description = "Drastically increases movement speed, but deals toxin damage."
|
||||
description = "Drastically increases movement speed."
|
||||
color = "#AE151D"
|
||||
metabolization_rate = 2.5 * REAGENTS_METABOLISM
|
||||
|
||||
@@ -1489,12 +1489,6 @@
|
||||
L.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/changelinghaste)
|
||||
..()
|
||||
|
||||
/datum/reagent/medicine/changelinghaste/on_mob_life(mob/living/carbon/metabolizer, delta_time, times_fired)
|
||||
metabolizer.adjustToxLoss(2 * REM * delta_time, 0)
|
||||
..()
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/reagent/medicine/corazone
|
||||
// Heart attack code will not do damage if corazone is present
|
||||
// because it's SPACE MAGIC ASPIRIN
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
name = "combat stimulant injector"
|
||||
desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat and get people back in the fight."
|
||||
amount_per_transfer_from_this = 10
|
||||
item_state = "combat_hypo"
|
||||
icon_state = "combat_hypo"
|
||||
volume = 100
|
||||
ignore_flags = 1 // So they can heal their comrades.
|
||||
@@ -69,17 +70,27 @@
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 30, /datum/reagent/medicine/omnizine = 30, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/atropine = 15)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/nanites
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical compounds for rapid healing."
|
||||
name = "experimental combat stimulant injector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical nanites and a stimulant for rapid healing and a combat boost."
|
||||
item_state = "nanite_hypo"
|
||||
icon_state = "nanite_hypo"
|
||||
volume = 100
|
||||
list_reagents = list(/datum/reagent/medicine/adminordrazine/quantum_heal = 80, /datum/reagent/medicine/synaptizine = 20)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/magillitis
|
||||
name = "experimental autoinjector"
|
||||
desc = "A modified air-needle autoinjector with a small single-use reservoir. It contains an experimental serum."
|
||||
icon_state = "combat_hypo"
|
||||
volume = 5
|
||||
reagent_flags = NONE
|
||||
list_reagents = list(/datum/reagent/magillitis = 5)
|
||||
/obj/item/reagent_containers/hypospray/combat/nanites/update_icon()
|
||||
if(reagents.total_volume > 0)
|
||||
icon_state = initial(icon_state)
|
||||
else
|
||||
icon_state = "[initial(icon_state)]0"
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/heresypurge
|
||||
name = "holy water piercing injector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water and pacifier mixture. Not for use on your teammates."
|
||||
item_state = "holy_hypo"
|
||||
icon_state = "holy_hypo"
|
||||
volume = 250
|
||||
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
|
||||
amount_per_transfer_from_this = 50
|
||||
|
||||
//MediPens
|
||||
|
||||
@@ -136,6 +147,8 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/ekit
|
||||
name = "emergency first-aid autoinjector"
|
||||
desc = "An epinephrine medipen with extra coagulant and antibiotics to help stabilize bad cuts and burns."
|
||||
icon_state = "healthpen"
|
||||
item_state = "healthpen"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5)
|
||||
@@ -143,15 +156,19 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/blood_loss
|
||||
name = "hypovolemic-response autoinjector"
|
||||
desc = "A medipen designed to stabilize and rapidly reverse severe bloodloss."
|
||||
icon_state = "hypovolemic"
|
||||
item_state = "hypovolemic"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 5, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/iron = 3.5, /datum/reagent/medicine/salglu_solution = 4)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/stimulants
|
||||
name = "illegal stimpack medipen"
|
||||
desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained"
|
||||
name = "stimpack medipen"
|
||||
desc = "Contains stimulants."
|
||||
icon_state = "syndipen"
|
||||
item_state = "syndipen"
|
||||
volume = 50
|
||||
amount_per_transfer_from_this = 10
|
||||
amount_per_transfer_from_this = 50
|
||||
list_reagents = list(/datum/reagent/medicine/stimulants = 50)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/stimulants/baseball
|
||||
@@ -166,6 +183,7 @@
|
||||
name = "stimpack medipen"
|
||||
desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor."
|
||||
icon_state = "stimpen"
|
||||
item_state = "stimpen"
|
||||
volume = 20
|
||||
amount_per_transfer_from_this = 20
|
||||
list_reagents = list(/datum/reagent/medicine/ephedrine = 10, /datum/reagent/consumable/coffee = 10)
|
||||
@@ -177,20 +195,79 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/morphine
|
||||
name = "morphine medipen"
|
||||
desc = "A rapid way to get you out of a tight situation and fast! You'll feel rather drowsy, though."
|
||||
icon_state = "morphen"
|
||||
item_state = "morphen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/morphine = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/penacid
|
||||
name = "pentetic acid medipen"
|
||||
desc = "A autoinjector containing pentetic acid, used to reduce high levels of radiations and moderate toxins."
|
||||
icon_state = "penacid"
|
||||
item_state = "penacid"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/pen_acid = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/atropine
|
||||
name = "atropine autoinjector"
|
||||
desc = "A rapid way to save a person from a critical injury state!"
|
||||
icon_state = "atropen"
|
||||
item_state = "atropen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/atropine = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/salacid
|
||||
name = "salicyclic acid medipen"
|
||||
desc = "A autoinjector containing salicyclic acid, used to treat severe brute damage."
|
||||
icon_state = "salacid"
|
||||
item_state = "salacid"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/sal_acid = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/oxandrolone
|
||||
name = "oxandrolone medipen"
|
||||
desc = "A autoinjector containing oxandrolone, used to treat severe burns."
|
||||
icon_state = "oxapen"
|
||||
item_state = "oxapen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/oxandrolone = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/salbutamol
|
||||
name = "salbutamol medipen"
|
||||
desc = "A autoinjector containing salbutamol, used to heal oxygen damage quickly."
|
||||
icon_state = "salpen"
|
||||
item_state = "salpen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/salbutamol = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure
|
||||
name = "BVAK autoinjector"
|
||||
desc = "Bio Virus Antidote Kit autoinjector. Has a two use system for yourself, and someone else. Inject when infected."
|
||||
icon_state = "stimpen"
|
||||
icon_state = "tbpen"
|
||||
item_state = "tbpen"
|
||||
volume = 60
|
||||
amount_per_transfer_from_this = 30
|
||||
list_reagents = list(/datum/reagent/medicine/atropine = 10, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/salbutamol = 20, /datum/reagent/medicine/spaceacillin = 20)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure/update_icon()
|
||||
if(reagents.total_volume > 30)
|
||||
icon_state = initial(icon_state)
|
||||
else if (reagents.total_volume > 0)
|
||||
icon_state = "[initial(icon_state)]1"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]0"
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/survival
|
||||
name = "survival medipen"
|
||||
desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards. WARNING: Do not inject more than one pen in quick succession."
|
||||
icon_state = "stimpen"
|
||||
icon_state = "minepen"
|
||||
item_state = "minepen"
|
||||
volume = 52
|
||||
amount_per_transfer_from_this = 52
|
||||
list_reagents = list(/datum/reagent/medicine/salbutamol = 10, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/neo_jelly = 15, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/lavaland_extract = 2)
|
||||
@@ -198,16 +275,21 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/firelocker
|
||||
name = "fire treatment medipen"
|
||||
desc = "A medipen that has been fulled with burn healing chemicals for personnel without advanced medical knowledge."
|
||||
icon_state = "firepen"
|
||||
item_state = "firepen"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/oxandrolone = 5, /datum/reagent/medicine/kelotane = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/heresypurge
|
||||
name = "holy water autoinjector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water mixture."
|
||||
volume = 250
|
||||
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
|
||||
amount_per_transfer_from_this = 50
|
||||
/obj/item/reagent_containers/hypospray/medipen/magillitis
|
||||
name = "experimental autoinjector"
|
||||
desc = "A custom-frame needle injector with a small single-use reservoir, containing an experimental serum. Unlike the more common medipen frame, it cannot pierce through protective armor or hardsuits, nor can the chemical inside be extracted."
|
||||
icon_state = "gorillapen"
|
||||
item_state = "gorillapen"
|
||||
volume = 5
|
||||
ignore_flags = 0
|
||||
reagent_flags = NONE
|
||||
list_reagents = list(/datum/reagent/magillitis = 5)
|
||||
|
||||
#define HYPO_SPRAY 0
|
||||
#define HYPO_INJECT 1
|
||||
|
||||
@@ -218,6 +218,31 @@
|
||||
volume = 50
|
||||
list_reagents = list(/datum/reagent/medicine/stimulants = 50)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband
|
||||
name = "unlabeled syringe"
|
||||
desc = "A syringe containing some sort of unknown chemical cocktail."
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/space_drugs
|
||||
list_reagents = list(/datum/reagent/drug/space_drugs = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/krokodil
|
||||
list_reagents = list(/datum/reagent/drug/krokodil = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/crank
|
||||
list_reagents = list(/datum/reagent/drug/crank = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/methamphetamine
|
||||
list_reagents = list(/datum/reagent/drug/methamphetamine = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/bath_salts
|
||||
list_reagents = list(/datum/reagent/drug/bath_salts = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/fentanyl
|
||||
list_reagents = list(/datum/reagent/toxin/fentanyl = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/contraband/morphine
|
||||
list_reagents = list(/datum/reagent/medicine/morphine = 15)
|
||||
|
||||
/obj/item/reagent_containers/syringe/calomel
|
||||
name = "syringe (calomel)"
|
||||
desc = "Contains calomel."
|
||||
|
||||
@@ -233,6 +233,14 @@
|
||||
anchored = TRUE
|
||||
reagent_id = /datum/reagent/consumable/cooking_oil
|
||||
|
||||
/obj/structure/reagent_dispensers/servingdish
|
||||
name = "serving dish"
|
||||
desc = "A dish full of food slop for your bowl."
|
||||
icon = 'icons/obj/kitchen.dmi'
|
||||
icon_state = "serving"
|
||||
anchored = TRUE
|
||||
reagent_id = /datum/reagent/consumable/nutraslop
|
||||
|
||||
////////
|
||||
//Kegs//
|
||||
////////
|
||||
|
||||
+10
-2
@@ -30,13 +30,21 @@
|
||||
category = list("initial","Dinnerware")
|
||||
|
||||
/datum/design/tray
|
||||
name = "Tray"
|
||||
id = "tray"
|
||||
name = "Serving Tray"
|
||||
id = "servingtray"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 3000)
|
||||
build_path = /obj/item/storage/bag/tray
|
||||
category = list("initial","Dinnerware")
|
||||
|
||||
/datum/design/tray
|
||||
name = "Cafeteria Tray"
|
||||
id = "foodtray"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 3000)
|
||||
build_path = /obj/item/storage/bag/tray/cafeteria
|
||||
category = list("initial","Dinnerware")
|
||||
|
||||
/datum/design/bowl
|
||||
name = "Bowl"
|
||||
id = "bowl"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user