Merge remote-tracking branch 'tgstation/master' into upstream-6-2-2026

This commit is contained in:
shayoki
2026-06-03 01:23:54 -05:00
1259 changed files with 50763 additions and 28726 deletions
@@ -629,8 +629,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
fishing_modifier = -21 //it only lives for 25 seconds, so we make them worth it.
custom_materials = null
/obj/structure/chair/mime/wrench_act_secondary(mob/living/user, obj/item/weapon)
return NONE
/obj/structure/chair/mime/Initialize(mapload)
. = ..()
AddElement(/datum/element/tool_blocker, TOOL_WRENCH, TOOL_ACT_SECONDARY)
/obj/structure/chair/mime/post_buckle_mob(mob/living/M)
M.add_offsets(type, z_add = 5)
@@ -159,11 +159,9 @@
content.forceMove(folding_bodybag)
if(isliving(content))
to_chat(content, span_userdanger("You're suddenly forced into a tiny, compressed space!"))
if(iscarbon(content))
var/mob/living/carbon/mob = content
if (mob.dna?.get_mutation(/datum/mutation/dwarfism))
max_weight_of_contents = max(WEIGHT_CLASS_NORMAL, max_weight_of_contents)
continue
if(HAS_TRAIT(content, TRAIT_DWARF))
max_weight_of_contents = max(WEIGHT_CLASS_NORMAL, max_weight_of_contents)
continue
if(!isitem(content))
max_weight_of_contents = max(WEIGHT_CLASS_BULKY, max_weight_of_contents)
continue
@@ -297,8 +295,8 @@
air_contents.temperature = T20C
air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
air_contents.gases[/datum/gas/oxygen][MOLES] = (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
air_contents.gases[/datum/gas/nitrogen][MOLES] = (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
air_contents.adjust_gas(/datum/gas/oxygen, (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD)
air_contents.adjust_gas(/datum/gas/nitrogen, (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
/obj/structure/closet/body_bag/environmental/nanotrasen
name = "elite environmental protection bag"
@@ -428,8 +426,8 @@
air_contents.temperature = T20C
air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrous_oxide)
air_contents.gases[/datum/gas/oxygen][MOLES] = (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
air_contents.adjust_gas(/datum/gas/oxygen, (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD)
air_contents.adjust_gas(/datum/gas/nitrous_oxide, (ONE_ATMOSPHERE*50)/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
/obj/structure/closet/body_bag/environmental/hardlight
name = "hardlight bodybag"
@@ -509,11 +507,10 @@
var/inner_temp = T0C - 60
air_contents = null
air_contents = new(mol_count)
air_contents.temperature = inner_temp
air_contents.set_temperature(inner_temp)
air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
air_contents.gases[/datum/gas/oxygen][MOLES] = (ONE_ATMOSPHERE * mol_count) / (R_IDEAL_GAS_EQUATION * inner_temp) * O2STANDARD
air_contents.gases[/datum/gas/nitrogen][MOLES] = (ONE_ATMOSPHERE * mol_count) / (R_IDEAL_GAS_EQUATION * inner_temp) * N2STANDARD
air_contents.set_gas(/datum/gas/oxygen, ((ONE_ATMOSPHERE * mol_count) / (R_IDEAL_GAS_EQUATION * inner_temp) * O2STANDARD))
air_contents.set_gas(/datum/gas/nitrogen, ((ONE_ATMOSPHERE * mol_count) / (R_IDEAL_GAS_EQUATION * inner_temp) * N2STANDARD))
/obj/structure/closet/body_bag/environmental/stasis/examine_status(mob/user)
switch(100 * get_integrity_percentage())
@@ -9,6 +9,7 @@
new /obj/item/radio/weather_monitor (src)
new /obj/item/radio/headset/heads/qm(src)
new /obj/item/megaphone/cargo(src)
new /obj/item/assembly/flash/handheld(src)
new /obj/item/tank/internals/emergency_oxygen(src)
new /obj/item/universal_scanner(src)
new /obj/item/door_remote/quartermaster(src)
@@ -210,6 +210,9 @@
req_one_access = list(ACCESS_BRIG)
var/id = null
/obj/structure/closet/secure_closet/brig/holodeck
req_one_access = COMMON_ACCESS
/obj/structure/closet/secure_closet/brig/genpop
name = "genpop storage locker"
desc = "Used for storing the belongings of genpop's tourists visiting the locals."
@@ -76,7 +76,7 @@
) = 11,
list( // Mobs
/mob/living/basic/bot/honkbot = 5,
/mob/living/basic/bot/secbot/honkbot = 5,
/mob/living/basic/pet/gondola = 2,
/obj/effect/spawner/abandoned_crate/bloodroaches = 1,
) = 8,
@@ -188,6 +188,12 @@
return ITEM_INTERACT_SUCCESS
/obj/structure/closet/crate/secure/loot/multitool_act_secondary(mob/living/user, obj/item/tool)
if(!locked)
return
attack_hand(user)
return ITEM_INTERACT_SUCCESS
/// Implements bulls and cows algorithm to compare guess against actual code
/obj/structure/closet/crate/secure/loot/proc/bulls_and_cows(guess)
var/bulls = 0
@@ -64,7 +64,7 @@
/obj/effect/spawner/abandoned_crate/space_suit
loot = list(
/obj/item/borg/upgrade/modkit/aoe/mobs = 1,
/obj/item/borg/upgrade/modkit/cooldown/aoe/mobs = 1,
/obj/item/clothing/suit/space = 1,
/obj/item/clothing/head/helmet/space = 1
)
+1 -1
View File
@@ -388,7 +388,7 @@
/obj/structure/displaycase/trophy/proc/toggle_historian_mode(mob/user)
historian_mode = !historian_mode
balloon_alert(user, "[historian_mode ? "enabled" : "disabled"] historian mode.")
playsound(src, 'sound/machines/beep/twobeep.ogg', vary = 50)
playsound(src, 'sound/machines/beep/twobeep.ogg', 10, vary = 50)
SStgui.update_uis(src)
/obj/structure/displaycase/trophy/toggle_lock(mob/user)
@@ -8,10 +8,14 @@
/obj/structure/chair/e_chair/Initialize(mapload)
. = ..()
var/obj/item/assembly/shock_kit/stored_kit = new(contents)
var/mutable_appearance/export_to_component = mutable_appearance('icons/obj/chairs.dmi', "echair_over", OBJ_LAYER, src, appearance_flags = KEEP_APART)
export_to_component = color_atom_overlay(export_to_component)
AddComponent(/datum/component/electrified_buckle, (SHOCK_REQUIREMENT_ITEM | SHOCK_REQUIREMENT_LIVE_CABLE | SHOCK_REQUIREMENT_SIGNAL_RECEIVED_TOGGLE), stored_kit, list(export_to_component))
AddComponent(\
/datum/component/electrified_buckle,\
overlays_to_add = list(export_to_component),\
shock_flags = (SHOCK_NOGLOVES)\
)
/obj/structure/chair/e_chair/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
if(W.tool_behaviour == TOOL_WRENCH)
+74 -1
View File
@@ -1059,9 +1059,82 @@
/obj/structure/flora/rock/pile/style_random/Initialize(mapload)
. = ..()
icon_state = "lavarocks[rand(1, 3)]"
icon_state = "lavarocks[pick(3;1,3;2,1;3)]"
update_appearance()
/obj/structure/flora/rock/pile/siderite
icon_state = "lavarocks_siderite1"
/obj/structure/flora/rock/pile/siderite/get_potential_products()
return list(/obj/item/stack/ore/glass/siderite = 1)
/obj/structure/flora/rock/pile/siderite/style_2
icon_state = "lavarocks_siderite2"
/obj/structure/flora/rock/pile/siderite/style_3
icon_state = "lavarocks_siderite3"
/obj/structure/flora/rock/pile/siderite/style_random/Initialize(mapload)
. = ..()
icon_state = "lavarocks_siderite[pick(3;1,3;2,1;3)]"
update_appearance()
/obj/structure/flora/rock/pile/shale
icon_state = "lavarocks_shale1"
/obj/structure/flora/rock/pile/shale/style_2
icon_state = "lavarocks_shale2"
/obj/structure/flora/rock/pile/shale/style_3
icon_state = "lavarocks_shale3"
/obj/structure/flora/rock/pile/shale/style_random/Initialize(mapload)
. = ..()
icon_state = "lavarocks_shale[pick(3;1,3;2,1;3)]"
update_appearance()
/obj/structure/flora/rock/siderite_growth
name = "siderite growth"
desc = "A natural stalagmite formed of siderite, with thin pointy strands of dirty metal sticking out of the top."
icon_state = "siderite_growth1"
base_icon_state = "siderite_growth"
icon = 'icons/obj/mining_zones/terrain.dmi'
harvest_message_med = "You finish mining the growth."
/obj/structure/flora/rock/siderite_growth/Initialize(mapload)
. = ..()
icon_state = "[base_icon_state][pick(3;1,3;2,1;3)]"
update_appearance()
AddComponent(/datum/component/seethrough, SEE_THROUGH_MAP_DEFAULT)
/obj/structure/flora/rock/siderite_growth/get_potential_products()
return list(/obj/item/stack/ore/glass/siderite = 1)
/obj/structure/flora/rock/volcano
name = "volcanic pore"
desc = "A miniature volcano-like rock formed of the lava slowly pouring from it."
icon_state = "volcano_1"
base_icon_state = "volcano"
harvest_message_med = "You finish mining the pore buildup."
light_range = 3
light_power = 2.5
light_color = LIGHT_COLOR_LAVA
/obj/structure/flora/rock/volcano/Initialize(mapload)
. = ..()
icon_state = "[base_icon_state]_[rand(1, 5)]"
update_appearance()
/obj/structure/flora/rock/volcano/update_overlays()
. = ..()
. += emissive_appearance(icon, "[icon_state]_e", src, alpha = 200)
/obj/structure/flora/rock/volcano/after_harvest(user)
var/turf/open/our_turf = loc
if (istype(our_turf))
our_turf.ChangeTurf(/turf/open/lava/smooth, flags = CHANGETURF_INHERIT_AIR)
return ..()
/obj/structure/flora/rock/pile/jungle
icon_state = "rock1"
icon = 'icons/obj/fluff/flora/jungleflora.dmi'
+14 -3
View File
@@ -31,6 +31,7 @@
/obj/structure/grille/Initialize(mapload)
. = ..()
AddElement(/datum/element/atmos_sensitive, mapload)
register_context()
/obj/structure/grille/Destroy()
update_cable_icons_on_turf(get_turf(src))
@@ -59,9 +60,19 @@
if(resistance_flags & INDESTRUCTIBLE)
return
if(anchored)
. += span_notice("It's secured in place with <b>screws</b>. The rods look like they could be <b>cut</b> through.")
. += span_notice("It's secured in place with [EXAMINE_HINT("screws")]. The rods look like they could be [EXAMINE_HINT("cut")] through.")
else
. += span_notice("The anchoring screws are <i>unscrewed</i>. The rods look like they could be <b>cut</b> through.")
. += span_notice("The anchoring screws are [EXAMINE_HINT("unscrewed")]. The rods look like they could be [EXAMINE_HINT("cut")] through.")
/obj/structure/grille/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(held_item?.tool_behaviour == TOOL_WIRECUTTER)
context[SCREENTIP_CONTEXT_RMB] = "Deconstruct"
return CONTEXTUAL_SCREENTIP_SET
if(held_item?.tool_behaviour == TOOL_SCREWDRIVER)
context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Unanchor" : "Anchor"]"
return CONTEXTUAL_SCREENTIP_SET
return .
/obj/structure/grille/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
@@ -199,7 +210,7 @@
return TRUE
return FALSE
/obj/structure/grille/wirecutter_act(mob/living/user, obj/item/tool)
/obj/structure/grille/wirecutter_act_secondary(mob/living/user, obj/item/tool)
add_fingerprint(user)
if(shock(user, 100))
return
+1 -2
View File
@@ -244,8 +244,7 @@
var/datum/species/S = victim.dna.species
if (istype(S))
victim.cut_overlays()
victim.update_body_parts_head_only()
victim.render_only_head()
victim.remove_overlay(BODY_ADJ_LAYER)
victim.pixel_y += -GUILLOTINE_HEAD_OFFSET // Offset their body so it looks like they're in the guillotine
victim.layer += GUILLOTINE_LAYER_DIFF
@@ -72,6 +72,7 @@
return
to_chat(user, span_notice("You discovered the geyser and mark it on the GPS system!"))
playsound(src, 'sound/machines/beep/twobeep_high.ogg', 30)
SEND_SIGNAL(user, COMSIG_LIVING_DISCOVERED_GEYSER, src)
if(discovery_message)
to_chat(user, discovery_message)
@@ -1,141 +0,0 @@
//Necropolis Tendrils, which spawn lavaland monsters and break into a chasm when killed
/obj/structure/spawner/lavaland
name = "necropolis tendril"
desc = "A vile tendril of corruption, originating deep underground. Terrible monsters are pouring out of it."
icon = 'icons/mob/simple/lavaland/nest.dmi'
icon_state = "tendril"
faction = list(FACTION_MINING, FACTION_ASHWALKER)
max_mobs = 3
max_integrity = 250
mob_types = list(/mob/living/basic/mining/watcher)
move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
anchored = TRUE
resistance_flags = FIRE_PROOF | LAVA_PROOF
var/obj/effect/light_emitter/tendril/emitted_light
scanner_taggable = TRUE
mob_gps_id = "WT"
spawner_gps_id = "Necropolis Tendril"
/obj/structure/spawner/lavaland/goliath
mob_types = list(/mob/living/basic/mining/goliath)
mob_gps_id = "GL"
/obj/structure/spawner/lavaland/legion
mob_types = list(/mob/living/basic/mining/legion/spawner_made)
mob_gps_id = "LG"
/obj/structure/spawner/lavaland/icewatcher
mob_types = list(/mob/living/basic/mining/watcher/icewing)
mob_gps_id = "WT|I" // icewing
GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/Initialize(mapload)
. = ..()
emitted_light = new(loc)
for(var/F in RANGE_TURFS(1, src))
if(ismineralturf(F))
var/turf/closed/mineral/M = F
M.ScrapeAway(null, CHANGETURF_IGNORE_AIR)
AddComponent(/datum/component/gps, "Eerie Signal")
GLOB.tendrils += src
/obj/structure/spawner/lavaland/atom_deconstruct(disassembled)
new /obj/effect/collapse(loc)
/obj/structure/spawner/lavaland/examine(mob/user)
var/list/examine_messages = ..()
examine_messages += span_notice("Once this thing gets hurts enough, it triggers a violent final retaliation.")
examine_messages += span_notice("You'll only have a few moments to run up, grab some loot with an open hand, and get out with it.")
return examine_messages
/obj/structure/spawner/lavaland/Destroy()
var/last_tendril = TRUE
if(GLOB.tendrils.len>1)
last_tendril = FALSE
if(last_tendril && !(flags_1 & ADMIN_SPAWNED_1))
if(SSachievements.achievements_enabled)
for(var/mob/living/L in view(7,src))
if(L.stat || !L.client)
continue
L.client.give_award(/datum/award/achievement/boss/tendril_exterminator, L)
L.client.give_award(/datum/award/score/tendril_score, L) //Progresses score by one
GLOB.tendrils -= src
QDEL_NULL(emitted_light)
return ..()
/obj/effect/light_emitter/tendril
set_luminosity = 4
set_cap = 2.5
light_color = LIGHT_COLOR_LAVA
/obj/effect/collapse
name = "collapsing necropolis tendril"
desc = "Get your loot and get clear!"
layer = TABLE_LAYER
icon = 'icons/mob/simple/lavaland/nest.dmi'
icon_state = "tendril"
anchored = TRUE
density = TRUE
/// weakref list of which mobs have gotten their loot from this effect.
var/list/collected = list()
/// a bit of light as to make less unfair deaths from the chasm
var/obj/effect/light_emitter/tendril/emitted_light
/obj/effect/collapse/Initialize(mapload)
. = ..()
emitted_light = new(loc)
visible_message(span_bolddanger("The tendril writhes in fury as the earth around it begins to crack and break apart! Get back!"))
balloon_alert_to_viewers("interact to grab loot before collapse!", vision_distance = 7)
playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, FALSE, 50, TRUE, TRUE)
addtimer(CALLBACK(src, PROC_REF(collapse)), 5 SECONDS)
/obj/effect/collapse/examine(mob/user)
var/list/examine_messages = ..()
if(isliving(user))
if(has_collected(user))
examine_messages += span_boldnotice("You've grabbed what you can, now get out!")
else
examine_messages += span_boldnotice("You might have some time to grab some goodies with an open hand before it collapses!")
return examine_messages
/obj/effect/collapse/attack_hand(mob/living/collector, list/modifiers)
. = ..()
if(has_collected(collector))
to_chat(collector, span_danger("You've already gotten some loot, just get out of there with it!"))
return
visible_message(span_warning("Something falls free of the tendril!"))
var/obj/structure/closet/crate/necropolis/tendril/loot = new /obj/structure/closet/crate/necropolis/tendril(loc)
collector.start_pulling(loot)
collected += WEAKREF(collector)
/obj/effect/collapse/Destroy()
collected.Cut()
QDEL_NULL(emitted_light)
return ..()
///Helper proc that resolves weakrefs to determine if collector is in collected list, returning a boolean.
/obj/effect/collapse/proc/has_collected(mob/collector)
for(var/datum/weakref/weakref as anything in collected)
var/mob/living/resolved = weakref.resolve()
//it could have been collector, it could not have been, we don't care
if(!resolved)
continue
if(resolved == collector)
return TRUE
return FALSE
/obj/effect/collapse/proc/collapse()
for(var/mob/M in range(7,src))
shake_camera(M, 15, 1)
playsound(get_turf(src),'sound/effects/explosion/explosionfar.ogg', 200, TRUE)
visible_message(span_bolddanger("The tendril falls inward, the ground around it widening into a yawning chasm!"))
for(var/turf/T in RANGE_TURFS(2,src))
if(HAS_TRAIT(T, TRAIT_NO_TERRAFORM))
continue
if(!T.density)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland, flags = CHANGETURF_INHERIT_AIR)
qdel(src)
@@ -1,4 +1,4 @@
#define MAX_ARTIFACT_ROLL_CHANCE 10
#define ARTIFACT_ROLL_CHANCE 7
#define MINERAL_TYPE_OPTIONS_RANDOM 4
#define OVERLAY_OFFSET_START 0
#define OVERLAY_OFFSET_EACH 5
@@ -72,7 +72,7 @@
/// What base icon_state do we use for this vent's boulders?
var/boulder_icon_state = "boulder"
/// Percent chance that this vent will produce an artifact boulder.
var/artifact_chance = 0
var/artifact_chance = ARTIFACT_ROLL_CHANCE
/// We use a cooldown to prevent the wave defense from being started multiple times.
COOLDOWN_DECLARE(wave_cooldown)
/// We use a cooldown to prevent players from tapping boulders rapidly from vents.
@@ -140,6 +140,7 @@
. = ..()
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
produce_boulder(TRUE)
return TRUE
/obj/structure/ore_vent/is_buckle_possible(mob/living/target, force, check_loc)
. = ..()
@@ -160,6 +161,8 @@
. += span_notice("This vent produces [span_bold("large")] boulders containing [ore_string]")
else
. += span_notice("This vent can be scanned with a [span_bold("Mining Scanner")].")
if(artifact_chance)
. += span_notice("This vent has a low chance to produce an [span_bold("artifact boulder.")] These may contain rare minerals or strange artifacts.")
/obj/structure/ore_vent/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(is_type_in_list(held_item, scanning_equipment))
@@ -221,8 +224,8 @@
* This also means a large boulder can highroll a boulder with a full stack of 50 sheets of material.
* @params ore_floor The number of minerals already rolled. Used to scale the logarithmic function.
*/
/obj/structure/ore_vent/proc/ore_quantity_function(ore_floor)
return SHEET_MATERIAL_AMOUNT * max(round(boulder_size * (log(rand(1 + ore_floor, 4 + ore_floor)) ** -1)), 1)
/obj/structure/ore_vent/proc/ore_quantity_function(ore_floor, ore_per_size = HALF_SHEET_MATERIAL_AMOUNT)
return ore_per_size * max(round(boulder_size * (log(rand(1 + ore_floor, 4 + ore_floor)) ** -1)), 1)
/**
* This confirms that the user wants to start the wave defense event, and that they can start it.
@@ -357,7 +360,7 @@
miner.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * boulder_size)
if(!user_id_card)
continue
var/point_reward_val = (MINER_POINT_MULTIPLIER * boulder_size) - MINER_POINT_MULTIPLIER // We remove the base value of discovering the vent
var/point_reward_val = (MINER_POINT_MULTIPLIER * (boulder_size + 2)) - MINER_POINT_MULTIPLIER // We remove the base value of discovering the vent
if(user_id_card.registered_account)
user_id_card.registered_account.mining_points += point_reward_val
user_id_card.registered_account.bank_card_talk("You have been awarded [point_reward_val] mining points for your efforts.")
@@ -525,6 +528,11 @@
var/atom/movable/flick_visual/visual = flick_overlay_view(mutable_appearance('icons/effects/vent_overlays.dmi', selected_mat.name), 4.5 SECONDS)
animate(visual, alpha = 0, time = 4.5 SECONDS, easing = CIRCULAR_EASING|EASE_IN)
if(artifact_chance)
var/atom/movable/flick_visual/rare = flick_overlay_view(mutable_appearance('icons/effects/vent_overlays.dmi', "rare_ore"), 4.5 SECONDS)
animate(rare, alpha = 0, time = 4.5 SECONDS, easing = CIRCULAR_EASING|EASE_IN)
/**
* Here is where we handle producing a new boulder, based on the qualities of this ore vent.
* Returns the boulder produced.
@@ -540,7 +548,8 @@
//produce the boulder
var/obj/item/boulder/new_rock
if(prob(artifact_chance))
new_rock = new /obj/item/boulder/artifact(loc)
var/picked_artifact = pick(typesof(/obj/item/boulder/artifact))
new_rock = new picked_artifact(loc)
else
new_rock = new /obj/item/boulder(loc)
Shake(duration = 1.5 SECONDS)
@@ -645,6 +654,10 @@
/datum/material/glass = 1,
)
/obj/structure/ore_vent/starter_resources/Initialize(mapload)
. = ..()
generate_description()
/obj/structure/ore_vent/random
// Todo: determine if we need a boulder_size default thats unique from the override performed in vent_size_setup.
@@ -653,7 +666,6 @@
if(!unique_vent && !mapload)
generate_mineral_breakdown(map_loading = mapload) //Default to random mineral breakdowns, unless this is a unique vent or we're still setting up default vent distribution.
generate_description()
artifact_chance = rand(0, MAX_ARTIFACT_ROLL_CHANCE)
if(!mapload)
vent_size_setup(random = TRUE) // We only do this here specific to random distribution ore vents, and within mapload we handle this manually within SSore_generation.
@@ -699,9 +711,9 @@
/datum/material/titanium = 1,
/datum/material/silver = 1,
/datum/material/gold = 1,
/datum/material/diamond = 1,
/datum/material/diamond = 0.1,
/datum/material/uranium = 1,
/datum/material/bluespace = 1,
/datum/material/bluespace = 0.1,
/datum/material/plastic = 1,
)
defending_mobs = list(
@@ -758,6 +770,41 @@
/mob/living/simple_animal/hostile/megafauna/colossus,
)
/obj/structure/ore_vent/debug
name = "debug ore vent"
desc = "How the hell did you get this?."
tapped = TRUE
discovered = TRUE
unique_vent = TRUE
color = "#ff00f2"
boulder_size = BOULDER_SIZE_SMALL
mineral_breakdown = list(
/datum/material/iron = 1,
)
/obj/structure/ore_vent/debug/attack_hand(mob/living/user, list/modifiers)
. = ..()
var/datum/material/choice = tgui_input_list(user, "Choose a material to add/remove.", "New material", subtypesof(/datum/material))
if(!choice)
return
if(mineral_breakdown[choice])
mineral_breakdown -= choice
balloon_alert_to_viewers("removed [choice::name]")
return
mineral_breakdown += choice
balloon_alert_to_viewers("added [choice::name]")
var/value = tgui_input_number(user, "What weight should it have?", "ore pickweight", 1, 100, 1)
mineral_breakdown[choice] = value
balloon_alert_to_viewers("weighting of [value] added")
generate_description()
/obj/structure/ore_vent/debug/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
var/choice = tgui_input_list(user, "Choose a vent size.", "New size", list(SMALL_VENT_TYPE, MEDIUM_VENT_TYPE, LARGE_VENT_TYPE))
if(!choice)
return
vent_size_setup(random = FALSE, force_size = choice, map_loading = FALSE)
/obj/effect/landmark/mining_center
name = "Mining Epicenter"
icon_state = "mining_epicenter"
@@ -772,7 +819,7 @@
GLOB.mining_center += loc
return INITIALIZE_HINT_QDEL
#undef MAX_ARTIFACT_ROLL_CHANCE
#undef ARTIFACT_ROLL_CHANCE
#undef MINERAL_TYPE_OPTIONS_RANDOM
#undef OVERLAY_OFFSET_START
#undef OVERLAY_OFFSET_EACH
+16 -6
View File
@@ -15,6 +15,9 @@
#define PRIDE_MIRROR_OPTIONS list(CHANGE_HAIR, CHANGE_BEARD, CHANGE_RACE, CHANGE_SEX, CHANGE_EYES)
#define MAGIC_MIRROR_OPTIONS list(CHANGE_HAIR, CHANGE_BEARD, CHANGE_RACE, CHANGE_SEX, CHANGE_EYES, CHANGE_NAME)
// Chance for the mirror to be haunted at creation
#define ROUNDSTART_CURSED_CHANCE 0.2
/obj/structure/mirror
name = "mirror"
desc = "Mirror mirror on the wall, who's the most robust of them all?"
@@ -29,7 +32,8 @@
///Can this mirror be removed from walls with tools?
var/deconstructable = TRUE
var/list/mirror_options = INERT_MIRROR_OPTIONS
// Can a revenant be imprisoned in this mirror?
var/cursable = TRUE
///Flags this race must have to be selectable with this type of mirror.
var/race_flags = MIRROR_MAGIC
///List of all Races that can be chosen, decided by its Initialize.
@@ -57,6 +61,8 @@
)
if(mapload)
find_and_mount_on_atom()
if(prob(ROUNDSTART_CURSED_CHANCE) && cursable)
AddComponent(/datum/component/revenant_prison, create_on_release = TRUE)
update_choices()
register_context()
@@ -72,10 +78,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
/obj/structure/mirror/broken
icon_state = "mirror_broke"
cursable = FALSE
broken = TRUE
desc = "Oh no, seven years of bad luck!"
/obj/structure/mirror/broken/Initialize(mapload)
. = ..()
atom_break(null, mapload)
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
@@ -262,7 +270,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
return
user.set_eye_color(sanitize_hexcolor(new_eye_color))
user.dna.update_ui_block(/datum/dna_block/identity/eye_colors)
user.update_body()
user.update_eyes()
to_chat(user, span_notice("You gaze at your new eyes with your new eyes. Perfect!"))
/obj/structure/mirror/examine(mob/user)
@@ -302,13 +310,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
to_chat(unlucky_dude, span_warning("A chill runs down your spine as [src] shatters..."))
unlucky_dude.AddComponent(/datum/component/omen, incidents_left = 7)
/obj/structure/mirror/atom_break(damage_flag, mapload)
/obj/structure/mirror/atom_break(damage_flag)
. = ..()
if(broken)
return
icon_state = "mirror_broke"
if(!mapload)
playsound(src, SFX_SHATTER, 70, TRUE)
playsound(src, SFX_SHATTER, 70, TRUE)
if(desc == initial(desc))
desc = "Oh no, seven years of bad luck!"
broken = TRUE
@@ -361,6 +368,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
icon_state = "magic_mirror"
mirror_options = MAGIC_MIRROR_OPTIONS
deconstructable = FALSE
cursable = FALSE
/obj/structure/mirror/magic/Initialize(mapload)
. = ..()
@@ -463,3 +471,5 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
#undef INERT_MIRROR_OPTIONS
#undef PRIDE_MIRROR_OPTIONS
#undef MAGIC_MIRROR_OPTIONS
#undef ROUNDSTART_CURSED_CHANCE
+40 -30
View File
@@ -463,35 +463,28 @@ GLOBAL_LIST_EMPTY(crematoriums)
/obj/structure/bodycontainer/crematorium/proc/cremate(mob/user)
if(locked)
return //don't let you cremate something twice or w/e
// Make sure we don't delete the actual morgue and its tray
var/list/conts = get_all_contents() - src - connected
if(!conts.len)
if(!length(conts))
audible_message(span_hear("You hear a hollow crackle."))
return
else
audible_message(span_hear("You hear a roar as the crematorium activates."))
audible_message(span_hear("You hear a roar as the crematorium activates."))
locked = TRUE
update_appearance()
locked = TRUE
update_appearance()
for(var/mob/living/victim in conts)
if(victim.incorporeal_move) //can't cook revenants!
continue
for(var/mob/living/M in conts)
if(M.incorporeal_move) //can't cook revenants!
continue
if (M.stat != DEAD)
M.emote("scream")
if(user)
log_combat(user, M, "cremated")
else
M.log_message("was cremated", LOG_ATTACK)
if (victim.stat != DEAD)
victim.emote("scream")
if(user.stat != DEAD)
user.investigate_log("has died from being cremated.", INVESTIGATE_DEATHS)
M.death(TRUE)
if(!QDELETED(M)) //some animals get automatically deleted on death.
M.ghostize()
qdel(M)
if(user)
log_combat(user, victim, "cremated")
else
victim.log_message("was cremated", LOG_ATTACK)
for(var/obj/O in conts) //conts defined above, ignores crematorium and tray
// SKYRAT EDIT ADDITION
@@ -502,15 +495,32 @@ GLOBAL_LIST_EMPTY(crematoriums)
continue
qdel(O)
if(!locate(/obj/effect/decal/cleanable/ash) in get_step(src, dir))//prevent pile-up
new/obj/effect/decal/cleanable/ash(src)
if(user.stat != DEAD)
user.investigate_log("has died from being cremated.", INVESTIGATE_DEATHS)
sleep(3 SECONDS)
victim.death(TRUE)
if(!QDELETED(victim)) //some animals get automatically deleted on death.
victim.ghostize()
qdel(victim)
if(!QDELETED(src))
locked = FALSE
update_appearance()
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE) //you horrible people
for(var/obj/to_destroy in conts) // conts defined above, ignores crematorium and tray
// Indestructible atoms should not be destroyed
if(istype(to_destroy, /obj/effect/dummy/phased_mob) || (to_destroy.resistance_flags & INDESTRUCTIBLE))
continue
qdel(to_destroy)
addtimer(CALLBACK(src, PROC_REF(unlock)), 3 SECONDS)
/obj/structure/bodycontainer/crematorium/proc/unlock()
if(QDELETED(src))
return
if(!locate(/obj/effect/decal/cleanable/ash) in get_step(src, dir)) // As to prevent pile-up
new /obj/effect/decal/cleanable/ash(src)
locked = FALSE
update_appearance()
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE) // You horrible people
/obj/structure/bodycontainer/crematorium/creamatorium
name = "creamatorium"
@@ -584,8 +594,8 @@ GLOBAL_LIST_EMPTY(crematoriums)
if(!istype(O, /obj/structure/closet/body_bag))
return
else
var/mob/M = O
if(M.buckled)
var/mob/victim = O
if(victim.buckled)
return
O.forceMove(src.loc)
if (user != O)
@@ -77,6 +77,7 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/obj/item/gun/magic/wand/door,
/obj/item/gun/magic/wand/nothing,
/obj/item/storage/belt/wands/full,
/obj/item/storage/belt/wands/full/discount,
/obj/item/gun/magic/staff/healing,
/obj/item/gun/magic/staff/chaos,
/obj/item/gun/magic/staff/door,
@@ -8,7 +8,7 @@
///Should we leave a brain behind when the statue is wrecked?
var/brain = TRUE
///Time left before the petrification ends and we let the mob free
var/timer = 48 SECONDS
var/timer = 8 MINUTES
///The mob that got medusa'd
var/mob/living/petrified_mob
@@ -37,7 +37,7 @@
/obj/structure/statue/petrified/process(seconds_per_tick)
if(!petrified_mob)
STOP_PROCESSING(SSobj, src)
timer -= seconds_per_tick
timer -= seconds_per_tick SECONDS
petrified_mob.Stun(4 SECONDS) //So they can't do anything while petrified
if(timer <= 0)
STOP_PROCESSING(SSobj, src)
@@ -0,0 +1,213 @@
#define INVESTIGATE_SIGNBOARD "signboard"
#define SIGNBOARD_WIDTH (ICON_SIZE_X * 3.5)
#define SIGNBOARD_HEIGHT (ICON_SIZE_Y * 2.5)
#define MAX_SIGN_LEN 106
/obj/structure/signboard
name = "sign"
desc = "A foldable sign."
icon = 'icons/obj/signboards.dmi'
icon_state = "sign"
base_icon_state = "sign"
density = TRUE
anchored = TRUE
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_REQUIRES_DEXTERITY
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
/// The image holding this sign's text
var/image/text_image
/// The current text written on the sign
var/sign_text
/// The maximum length of text that can be input onto the sign.
var/max_length = MAX_SIGN_LEN
/// If true, the text cannot be changed by players.
var/locked = FALSE
/// If text should be shown while unanchored.
var/show_while_unanchored = FALSE
/// If TRUE, the sign can be edited without a pen.
var/edit_by_hand = FALSE
/obj/structure/signboard/Initialize(mapload)
. = ..()
if(sign_text)
set_text(sign_text, force = TRUE)
investigate_log("had its text set on load to \"[sign_text]\"", INVESTIGATE_SIGNBOARD)
update_appearance()
register_context()
/obj/structure/signboard/Destroy(force)
QDEL_NULL(text_image)
return ..()
/obj/structure/signboard/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(is_locked(user))
return
if(held_item?.tool_behaviour == TOOL_WRENCH)
context[SCREENTIP_CONTEXT_LMB] = anchored ? "Unsecure" : "Secure"
return CONTEXTUAL_SCREENTIP_SET
if((edit_by_hand || istype(held_item, /obj/item/pen)) && (anchored || show_while_unanchored))
context[SCREENTIP_CONTEXT_LMB] = "Set Displayed Text"
if(sign_text)
context[SCREENTIP_CONTEXT_ALT_RMB] = "Clear Sign"
return CONTEXTUAL_SCREENTIP_SET
/obj/structure/signboard/examine(mob/user)
. = ..()
if(!edit_by_hand)
. += span_notice("You can write on the sign with a <b>pen.</b>")
if(anchored)
. += span_notice("It's secured to the floor, you could use a <b>wrench</b> to unsecure and move it.")
else
. += span_notice("It's unsecured, you could use a <b>wrench</b> to secure it in place.")
if(sign_text)
. += span_boldnotice("\nIt currently displays the following:")
. += span_info(html_encode(sign_text))
else
. += span_notice("\nIt's blank.")
/obj/structure/signboard/update_icon_state()
. = ..()
icon_state = "[base_icon_state][sign_text ? "" : "_blank"]"
/obj/structure/signboard/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, sign_text))
if(!set_text(var_value, force = TRUE))
return FALSE
datum_flags |= DF_VAR_EDITED
return TRUE
return ..()
/obj/structure/signboard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/pen))
return NONE
try_set_text(user)
return ITEM_INTERACT_SUCCESS
/obj/structure/signboard/attack_hand(mob/living/user, list/modifiers)
. = ..()
if(.)
return
if(!edit_by_hand && !user.is_holding_item_of_type(/obj/item/pen))
balloon_alert(user, "need a pen!")
return TRUE
if(try_set_text(user))
return TRUE
/obj/structure/signboard/proc/try_set_text(mob/living/user)
. = FALSE
if(!anchored && !show_while_unanchored)
return FALSE
if(check_locked(user))
return FALSE
var/new_text = tgui_input_text(
user,
message = "What would you like to set this sign's text to?",
title = full_capitalize(name),
default = sign_text,
max_length = max_length,
multiline = TRUE,
encode = FALSE,
)
if(QDELETED(src) || !new_text || check_locked(user))
return FALSE
var/list/filter_result = CAN_BYPASS_FILTER(user) ? null : is_ic_filtered(new_text)
if(filter_result)
REPORT_CHAT_FILTER_TO_USER(user, filter_result)
return FALSE
var/list/soft_filter_result = CAN_BYPASS_FILTER(user) ? null : is_soft_ic_filtered(new_text)
if(soft_filter_result)
if(tgui_alert(user, "Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return FALSE
message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" when writing to the sign at [ADMIN_VERBOSEJMP(src)], they may be using a disallowed term. Sign text: \"[html_encode(new_text)]\"")
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" when writing to the sign at [loc_name(src)], they may be using a disallowed term. Sign text: \"[new_text]\"")
if(set_text(new_text))
balloon_alert(user, "set text")
investigate_log("([key_name(user)]) set text to \"[sign_text || "(none)"]\"", INVESTIGATE_SIGNBOARD)
return TRUE
/obj/structure/signboard/click_alt_secondary(mob/user)
. = ..()
if(!sign_text || !can_interact(user) || !user.can_perform_action(src, NEED_DEXTERITY))
return
if(!edit_by_hand && !user.is_holding_item_of_type(/obj/item/pen))
balloon_alert(user, "need a pen!")
return
if(check_locked(user))
return
if(set_text(null))
balloon_alert(user, "cleared text")
investigate_log("([key_name(user)]) cleared the text", INVESTIGATE_SIGNBOARD)
/obj/structure/signboard/wrench_act(mob/living/user, obj/item/tool)
. = ..()
if(!anchored || !check_locked(user))
default_unfasten_wrench(user, tool)
return ITEM_INTERACT_SUCCESS
/obj/structure/signboard/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(!same_z_layer)
SET_PLANE_EXPLICIT(text_image, ABOVE_GAME_PLANE, src)
return ..()
/obj/structure/signboard/set_anchored(anchorvalue)
. = ..()
update_text()
/obj/structure/signboard/proc/is_locked(mob/user)
if(isAdminGhostAI(user))
return FALSE
return locked
/obj/structure/signboard/proc/check_locked(mob/user, silent = FALSE)
. = is_locked(user)
if(. && !silent)
balloon_alert(user, "locked!")
/obj/structure/signboard/proc/should_display_text()
if(QDELETED(src) || !isturf(loc) || !sign_text)
return FALSE
if(!anchored && !show_while_unanchored)
return FALSE
return TRUE
/obj/structure/signboard/MouseEntered(location, control, params)
. = ..()
if(QDELETED(src) || !should_display_text())
return
usr.client.images |= text_image
/obj/structure/signboard/MouseExited(location, control, params)
. = ..()
usr.client.images -= text_image
/// Creates [text_image] if it doesn't exist, and sets its maptext to [sign_text]
/obj/structure/signboard/proc/update_text()
PROTECTED_PROC(TRUE) // Use set_text instead
var/safe_width = src.bound_width || ICON_SIZE_X
var/safe_height = src.bound_height || ICON_SIZE_Y
var/text_html = MAPTEXT_GRAND9K("<span style='text-align: center; line-height: 1'>[html_encode(sign_text)]</span>")
if(!text_image)
text_image = new
SET_PLANE_EXPLICIT(text_image, ABOVE_GAME_PLANE, src)
text_image.alpha = 192
text_image.loc = src
text_image.maptext_x = (SIGNBOARD_WIDTH - safe_width) * -0.5
text_image.maptext_y = safe_height
text_image.maptext_width = SIGNBOARD_WIDTH
text_image.maptext_height = SIGNBOARD_HEIGHT
text_image.maptext = text_html
/obj/structure/signboard/proc/set_text(new_text, force = FALSE)
. = FALSE
if(QDELETED(src) || (locked && !force))
return
if(!istext(new_text) && !isnull(new_text))
CRASH("Attempted to set invalid signtext: [new_text]")
. = TRUE
sign_text = trim(new_text, max_length)
update_text()
update_appearance()
#undef MAX_SIGN_LEN
#undef SIGNBOARD_HEIGHT
#undef SIGNBOARD_WIDTH
@@ -0,0 +1,239 @@
/obj/structure/signboard/holosign
name = "holographic sign"
desc = "A holographic signboard, projecting text above it."
icon_state = "holographic_sign"
base_icon_state = "holographic_sign"
layer = ABOVE_MOB_LAYER
density = FALSE
edit_by_hand = TRUE
show_while_unanchored = TRUE
light_range = MINIMUM_USEFUL_LIGHT_RANGE
light_power = 0.3
light_color = COLOR_CARP_TEAL
light_on = FALSE
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5.05, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.7)
/// If set, only IDs with this name can (un)lock the sign.
var/registered_owner
/// The current color of the sign.
/// The sign will be greyscale if this is set.
var/current_color
/obj/structure/signboard/holosign/Initialize(mapload)
. = ..()
if(current_color)
INVOKE_ASYNC(src, PROC_REF(set_color), current_color)
AddComponent(/datum/component/usb_port, list(
/obj/item/circuit_component/holo_signboard,
))
/obj/structure/signboard/holosign/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
var/locked = is_locked(user)
if(istype(held_item, /obj/item/card/emag))
context[SCREENTIP_CONTEXT_LMB] = "Short Out Locking Mechanisms"
. = CONTEXTUAL_SCREENTIP_SET
else if(!locked && istype(held_item?.GetID(), /obj/item/usb_cable))
context[SCREENTIP_CONTEXT_LMB] = "Connect USB Cable"
else if(!locked && istype(held_item?.GetID(), /obj/item/card/id))
context[SCREENTIP_CONTEXT_LMB] = registered_owner ? "Remove ID Lock" : "Lock To ID"
. = CONTEXTUAL_SCREENTIP_SET
if(!locked)
context[SCREENTIP_CONTEXT_RMB] = "Set Sign Color"
. = CONTEXTUAL_SCREENTIP_SET
/obj/structure/signboard/holosign/update_icon_state()
base_icon_state = current_color ? "[initial(base_icon_state)]_greyscale" : initial(base_icon_state)
. = ..()
if(obj_flags & EMAGGED)
icon_state += "_emag"
/obj/structure/signboard/holosign/examine(mob/user)
. = ..()
if(obj_flags & EMAGGED)
. += span_warning("<br>Its locking mechanisms appear to be shorted out!")
else if(registered_owner)
. += span_info("<br>It is locked to the ID of [span_name(registered_owner)].")
/obj/structure/signboard/holosign/update_overlays()
. = ..()
if(sign_text)
. += emissive_appearance(icon, "holographic_sign_e", src)
/obj/structure/signboard/holosign/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, color) || var_name == NAMEOF(src, current_color))
INVOKE_ASYNC(src, PROC_REF(set_color), var_value)
datum_flags |= DF_VAR_EDITED
return TRUE
return ..()
/obj/structure/signboard/holosign/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
var/obj/item/item = astype(tool, /obj/item)
var/obj/item/card/id/id = item?.GetID()
if(!istype(id) || !can_interact(user) || !user.can_perform_action(src, NEED_DEXTERITY))
return NONE
var/trimmed_id_name = trimtext(id.registered_name)
if(!trimmed_id_name)
balloon_alert(user, "no name on id!")
return ITEM_INTERACT_BLOCKING
if(obj_flags & EMAGGED)
balloon_alert(user, "lock shorted out!")
return ITEM_INTERACT_BLOCKING
if(registered_owner)
if(!check_locked(user))
registered_owner = null
balloon_alert(user, "id lock removed")
investigate_log("([key_name(user)]) removed id lock", INVESTIGATE_SIGNBOARD)
else
registered_owner = trimmed_id_name
balloon_alert(user, "locked to id")
investigate_log("([key_name(user)]) added id lock for \"[registered_owner]\"", INVESTIGATE_SIGNBOARD)
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/signboard/holosign/is_locked(mob/living/user)
. = ..()
if(.)
return
if(registered_owner && isliving(user))
var/obj/item/card/id/id = user.get_idcard()
if(!istype(id) || QDELING(id))
return TRUE
return !cmptext(trimtext(id.registered_name), registered_owner)
/obj/structure/signboard/holosign/set_text(new_text, force)
. = ..()
set_light(l_on = !!sign_text)
/obj/structure/signboard/holosign/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(try_set_color(user))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/structure/signboard/holosign/proc/try_set_color(mob/user)
. = TRUE
if(!can_interact(user) || !user.can_perform_action(src, NEED_DEXTERITY))
return FALSE
if(check_locked(user))
return
var/new_color = sanitize_color(tgui_color_picker(user, "Set Sign Color", full_capitalize(name), current_color))
if(new_color && is_color_dark_with_saturation(new_color, 25))
balloon_alert(user, "color too dark!")
return
if(check_locked(user))
return
INVOKE_ASYNC(src, PROC_REF(set_color), new_color)
if(new_color)
balloon_alert(user, "set color to [new_color]")
investigate_log("([key_name(user)]) set the color to [new_color || "(none)"]", INVESTIGATE_SIGNBOARD)
else
balloon_alert(user, "unset color")
investigate_log("([key_name(user)]) cleared the color", INVESTIGATE_SIGNBOARD)
/obj/structure/signboard/holosign/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
return FALSE
playsound(src, SFX_SPARKS, vol = 100, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
do_sparks(3, cardinal_only = FALSE, source = src)
balloon_alert(user, "lock broken")
investigate_log("was emagged by [key_name(user)] (previous owner: [registered_owner || "(none)"])", INVESTIGATE_SIGNBOARD)
registered_owner = null
obj_flags |= EMAGGED
update_appearance()
/obj/structure/signboard/holosign/proc/sanitize_color(color)
. = sanitize_hexcolor(color)
if(!. || . == COLOR_BLACK)
return null
/obj/structure/signboard/holosign/proc/set_color(new_color)
new_color = sanitize_color(new_color)
if(!new_color)
current_color = null
remove_atom_colour(FIXED_COLOUR_PRIORITY)
else
current_color = new_color
add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
set_light(l_color = current_color || src::light_color)
update_appearance()
/obj/item/circuit_component/holo_signboard
display_name = "Holographic Signboard"
desc = "Output text to a signboard, insert <br> in the message field to linebreak. Set the color to 0, 0, 0 to reset to default."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/input/message
var/datum/port/input/clear
var/datum/port/output/fail_reason
var/datum/port/output/on_fail
var/datum/port/input/red
var/datum/port/input/green
var/datum/port/input/blue
var/datum/port/input/set_color
var/obj/structure/signboard/holosign/connected_display
/obj/item/circuit_component/holo_signboard/populate_ports()
message = add_input_port("Message", PORT_TYPE_STRING)
clear = add_input_port("Clear", PORT_TYPE_SIGNAL, trigger = PROC_REF(clear_received))
fail_reason = add_output_port("Fail Reason", PORT_TYPE_STRING)
on_fail = add_output_port("Failed", PORT_TYPE_SIGNAL)
red = add_input_port("Red", PORT_TYPE_NUMBER)
green = add_input_port("Green", PORT_TYPE_NUMBER)
blue = add_input_port("Blue", PORT_TYPE_NUMBER)
set_color = add_input_port("Set Color", PORT_TYPE_SIGNAL, trigger = PROC_REF(color_received))
/obj/item/circuit_component/holo_signboard/register_usb_parent(atom/movable/shell)
. = ..()
if(istype(shell, /obj/structure/signboard/holosign))
connected_display = shell
/obj/item/circuit_component/holo_signboard/input_received(datum/port/input/port)
if(!connected_display)
return
if(length(message.value) > connected_display.max_length) //5000 is a hell of a lot longer than 144.
fail_reason.set_output("Too long ([length(message.value)]/[connected_display.max_length]).")
on_fail.set_output(COMPONENT_SIGNAL)
return
if(is_ic_filtered(message.value))
fail_reason.set_output("Prohibited content.")
on_fail.set_output(COMPONENT_SIGNAL)
return
var/edited_message = replacetextEx_char(message.value, "<br>", "\n")
if(connected_display.set_text(edited_message))
investigate_log("Circuit USB ([parent.get_creator()]) set text to \"[connected_display.sign_text || "(none)"]\"", INVESTIGATE_SIGNBOARD)
if(is_soft_ic_filtered(message.value))
message_admins("A circuit component (by [parent.get_creator_admin()]) added a soft filtered message to a signboard. [ADMIN_COORDJMP(src)]")
else
fail_reason.set_output("Connection refused by external endpoint.")
on_fail.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/holo_signboard/proc/clear_received(datum/port/input/port)
if(!connected_display.set_text(null))
fail_reason.set_output("Connection refused by external endpoint.")
on_fail.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/holo_signboard/proc/color_received(datum/port/input/port)
red.set_value(clamp(red.value, 0, 255))
blue.set_value(clamp(blue.value, 0, 255))
green.set_value(clamp(green.value, 0, 255))
var/signboard_color = rgb(red.value, green.value, blue.value)
if(signboard_color && signboard_color != rgb(0, 0, 0) && is_color_dark_with_saturation(signboard_color, 25))
fail_reason.set_output("Color too dark to display.")
on_fail.set_output(COMPONENT_SIGNAL)
return
connected_display.set_color(signboard_color) //doesnt have a return so no need to check and error
investigate_log("Circuit USB ([parent.get_creator()]) set the color to [signboard_color || "(none)"]", INVESTIGATE_SIGNBOARD)
/// Given a color in the format of "#RRGGBB", will return if the color
/// is dark. Value is mixed with Saturation and Brightness from HSV.
/proc/is_color_dark_with_saturation(color, threshold = 25)
var/hsl = rgb2num(color, COLORSPACE_HSL)
return hsl[3] < threshold
@@ -1,14 +1,15 @@
/obj/structure/sign/clock
name = "wall clock"
desc = "It's your run-of-the-mill wall clock showing both the local Coalition Standard Time and the galactic Treaty Coordinated Time. Perfect for staring at instead of working."
desc = "It's your run-of-the-mill wall clock showing both the local Nanotrasen Standard Time and the galactic Treaty Coordinated Time. Perfect for staring at instead of working."
icon_state = "clock"
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/clock, 32)
/obj/structure/sign/clock/examine(mob/user)
. = ..()
. += span_info("The current CST (local) time is: [station_time_timestamp()].")
. += span_info("The current TCT (galactic) time is: [time2text(world.realtime, "hh:mm:ss", 0)].")
. += span_info("The current NST (local) time is: [server_timestamp(ic_time = TRUE, twelve_hour_clock = user.client?.prefs.read_preference(/datum/preference/toggle/twelve_hour))].")
if(user.is_literate())
. += span_info("That means it is currently [round_timestamp()] into the shift.")
/obj/structure/sign/calendar
name = "wall calendar"
+1 -1
View File
@@ -95,7 +95,7 @@
return FALSE
new /obj/item/flamethrower(get_turf(src))
var/obj/item/tank/internals/plasma/ptank = new /obj/item/tank/internals/plasma(get_turf(src))
ptank.air_contents.gases[/datum/gas/plasma][MOLES] = (0)
ptank.air_contents.set_gas(/datum/gas/plasma, 0)
drop_custom_materials()
qdel(src)
return TRUE
@@ -14,9 +14,8 @@
/obj/structure/transit_tube_pod/Initialize(mapload)
. = ..()
air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
air_contents.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD
air_contents.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD
var/list/new_gases = list(/datum/gas/oxygen = MOLES_O2STANDARD, /datum/gas/nitrogen = MOLES_N2STANDARD)
air_contents.adjust_multiple_gases(new_gases)
air_contents.temperature = T20C
/obj/structure/transit_tube_pod/Destroy()
+6 -11
View File
@@ -849,17 +849,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/tinted/frosted/spaw
flags_1 = PREVENT_CLICK_UNDER_1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/structure/window/reinforced/shuttle/indestructible/welder_act(mob/living/user, obj/item/tool)
return NONE
/obj/structure/window/reinforced/shuttle/indestructible/screwdriver_act(mob/living/user, obj/item/tool)
return NONE
/obj/structure/window/reinforced/shuttle/indestructible/wrench_act(mob/living/user, obj/item/tool)
return NONE
/obj/structure/window/reinforced/shuttle/indestructible/crowbar_act(mob/living/user, obj/item/tool)
return NONE
/obj/structure/window/reinforced/shuttle/indestructible/Initialize(mapload)
. = ..()
AddElement(/datum/element/tool_blocker, TOOL_WELDER, TOOL_ACT_PRIMARY)
AddElement(/datum/element/tool_blocker, TOOL_SCREWDRIVER, TOOL_ACT_PRIMARY)
AddElement(/datum/element/tool_blocker, TOOL_WRENCH, TOOL_ACT_PRIMARY)
AddElement(/datum/element/tool_blocker, TOOL_CROWBAR, TOOL_ACT_PRIMARY)
/obj/structure/window/reinforced/plasma/plastitanium
name = "plastitanium window"