Merge branch 'master' into random-engine

This commit is contained in:
Putnam3145
2020-01-08 21:08:20 -08:00
committed by GitHub
106 changed files with 1224 additions and 483 deletions
+1 -3
View File
@@ -50,9 +50,7 @@
/area/survivalpod)
"l" = (
/obj/structure/tubes,
/obj/structure/chair/comfy/black{
dir = 8
},
/obj/machinery/recharge_station,
/turf/open/floor/pod,
/area/survivalpod)
"m" = (
+2 -4
View File
@@ -67,8 +67,7 @@
layer = 3
},
/obj/machinery/door/window/survival_pod{
dir = 1;
icon_state = "windoor"
dir = 1
},
/turf/open/floor/carpet/black,
/area/survivalpod)
@@ -133,7 +132,6 @@
/area/survivalpod)
"u" = (
/obj/machinery/door/window/survival_pod{
icon_state = "windoor";
dir = 1
},
/turf/open/floor/carpet/black,
@@ -169,10 +167,10 @@
/area/survivalpod)
"y" = (
/obj/structure/sink/kitchen{
icon_state = "sink_alt";
dir = 4;
pixel_x = -13
},
/obj/machinery/recharge_station/upgraded,
/turf/open/floor/carpet/black,
/area/survivalpod)
"z" = (
+5 -1
View File
@@ -268,6 +268,10 @@
/obj/structure/fans/tiny,
/turf/open/floor/carpet/black,
/area/survivalpod)
"T" = (
/obj/machinery/recharge_station/fullupgrade,
/turf/open/floor/carpet/black,
/area/survivalpod)
(1,1,1) = {"
a
@@ -287,7 +291,7 @@ b
f
q
s
x
T
y
D
F
+1
View File
@@ -47,6 +47,7 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_FAIL2TOPIC 22
#define INIT_ORDER_TITLE 20
#define INIT_ORDER_GARBAGE 19
#define INIT_ORDER_DBCORE 18
+3 -2
View File
@@ -78,7 +78,6 @@
if (CONFIG_GET(flag/log_manifest))
WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")
/proc/log_say(text)
if (CONFIG_GET(flag/log_say))
WRITE_LOG(GLOB.world_game_log, "SAY: [text]")
@@ -121,7 +120,6 @@
if (CONFIG_GET(flag/log_vote))
WRITE_LOG(GLOB.world_game_log, "VOTE: [text]")
/proc/log_topic(text)
WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]")
@@ -141,6 +139,9 @@
if (CONFIG_GET(flag/log_job_debug))
WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]")
/proc/log_subsystem(subsystem, text)
WRITE_LOG(GLOB.subsystem_log, "[subsystem]: [text]")
/* Log to both DD and the logfile. */
/proc/log_world(text)
#ifdef USE_CUSTOM_ERROR_HANDLER
+7
View File
@@ -43,6 +43,13 @@
var/static/blacklisted_areas = typecacheof(list(
/area/space,
))
if(creator)
if(creator.create_area_cooldown >= world.time)
to_chat(creator, "<span class='warning'>You're trying to create a new area a little too fast.</span>")
return
creator.create_area_cooldown = world.time + 10
var/list/turfs = detect_room(get_turf(creator), area_or_turf_fail_types)
if(!turfs)
to_chat(creator, "<span class='warning'>The new area must be completely airtight and not a part of a shuttle.</span>")
+2
View File
@@ -30,6 +30,8 @@ GLOBAL_VAR(world_virus_log)
GLOBAL_PROTECT(world_virus_log)
GLOBAL_VAR(world_map_error_log)
GLOBAL_PROTECT(world_map_error_log)
GLOBAL_VAR(subsystem_log)
GLOBAL_PROTECT(subsystem_log)
GLOBAL_LIST_EMPTY(bombers)
GLOBAL_PROTECT(bombers)
@@ -236,7 +236,6 @@
if(!(M.config_tag in modes)) // ensure each mode is added only once
modes += M.config_tag
mode_names[M.config_tag] = M.name
probabilities[M.config_tag] = M.probability
mode_reports[M.config_tag] = M.generate_report()
if(probabilities[M.config_tag]>0)
mode_false_report_weight[M.config_tag] = M.false_report_weight
@@ -342,6 +341,9 @@
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(M.config_tag in SSvote.stored_modetier_results && SSvote.stored_modetier_results[M.config_tag] < Get(/datum/config_entry/number/dropped_modes))
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
@@ -0,0 +1,19 @@
/datum/config_entry/number/fail2topic_rate_limit
config_entry_value = 10 //deciseconds
/datum/config_entry/number/fail2topic_max_fails
config_entry_value = 5
/datum/config_entry/string/fail2topic_rule_name
config_entry_value = "_dd_fail2topic"
protection = CONFIG_ENTRY_LOCKED //affects physical server configuration, no touchies!!
/datum/config_entry/flag/fail2topic_enabled
config_entry_value = TRUE
/datum/config_entry/number/topic_max_size
config_entry_value = 8192
/datum/config_entry/keyed_list/topic_rate_limit_whitelist
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_FLAG
@@ -394,3 +394,9 @@
/datum/config_entry/flag/allow_clockwork_marauder_on_station
config_entry_value = TRUE
/datum/config_entry/flag/modetier_voting
config_entry_value = TRUE
/datum/config_entry/number/dropped_modes
config_entry_value = 3
+2 -2
View File
@@ -54,7 +54,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/static/restart_clear = 0
var/static/restart_timeout = 0
var/static/restart_count = 0
var/static/random_seed
//current tick limit, assigned before running a subsystem.
@@ -69,7 +69,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
if(!random_seed)
random_seed = (TEST_RUN_PARAMETER in world.params) ? 29051994 : rand(1, 1e9)
rand_seed(random_seed)
var/list/_subsystems = list()
subsystems = _subsystems
if (Master != src)
+3 -1
View File
@@ -155,6 +155,8 @@
if(SS_SLEEPING)
state = SS_PAUSING
/datum/controller/subsystem/proc/subsystem_log(msg)
return log_subsystem(name, msg)
//used to initialize the subsystem AFTER the map has loaded
/datum/controller/subsystem/Initialize(start_timeofday)
@@ -162,7 +164,7 @@
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
to_chat(world, "<span class='boldannounce'>[msg]</span>")
log_world(msg)
log_subsystem("INIT", msg)
return time
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
+113
View File
@@ -0,0 +1,113 @@
SUBSYSTEM_DEF(fail2topic)
name = "Fail2Topic"
init_order = INIT_ORDER_FAIL2TOPIC
flags = SS_BACKGROUND
runlevels = ALL
var/list/rate_limiting = list()
var/list/fail_counts = list()
var/list/active_bans = list()
var/rate_limit
var/max_fails
var/rule_name
var/enabled = FALSE
/datum/controller/subsystem/fail2topic/Initialize(timeofday)
rate_limit = CONFIG_GET(number/fail2topic_rate_limit)
max_fails = CONFIG_GET(number/fail2topic_max_fails)
rule_name = CONFIG_GET(string/fail2topic_rule_name)
enabled = CONFIG_GET(flag/fail2topic_enabled)
DropFirewallRule() // Clear the old bans if any still remain
if (world.system_type == UNIX && enabled)
enabled = FALSE
subsystem_log("DISABLED - UNIX systems are not supported.")
if(!enabled)
flags |= SS_NO_FIRE
can_fire = FALSE
return ..()
/datum/controller/subsystem/fail2topic/fire()
while (rate_limiting.len)
var/ip = rate_limiting[1]
var/last_attempt = rate_limiting[ip]
if (world.time - last_attempt > rate_limit)
rate_limiting -= ip
fail_counts -= ip
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/fail2topic/Shutdown()
DropFirewallRule()
/datum/controller/subsystem/fail2topic/proc/IsRateLimited(ip)
var/last_attempt = rate_limiting[ip]
var/static/datum/config_entry/keyed_list/topic_rate_limit_whitelist/cached_whitelist_entry
if(!istype(cached_whitelist_entry))
cached_whitelist_entry = CONFIG_GET(keyed_list/topic_rate_limit_whitelist)
if(istype(cached_whitelist_entry))
if(cached_whitelist_entry.config_entry_value[ip])
return FALSE
if (active_bans[ip])
return TRUE
rate_limiting[ip] = world.time
if (isnull(last_attempt))
return FALSE
if (world.time - last_attempt > rate_limit)
fail_counts -= ip
return FALSE
else
var/failures = fail_counts[ip]
if (isnull(failures))
fail_counts[ip] = 1
return TRUE
else if (failures > max_fails)
BanFromFirewall(ip)
return TRUE
else
fail_counts[ip] = failures + 1
return TRUE
/datum/controller/subsystem/fail2topic/proc/BanFromFirewall(ip)
if (!enabled)
return
active_bans[ip] = world.time
fail_counts -= ip
rate_limiting -= ip
. = shell("netsh advfirewall firewall add rule name=\"[rule_name]\" dir=in interface=any action=block remoteip=[ip]")
if (.)
subsystem_log("Failed to ban [ip]. Exit code: [.].")
else if (isnull(.))
subsystem_log("Failed to invoke shell to ban [ip].")
else
subsystem_log("Banned [ip].")
/datum/controller/subsystem/fail2topic/proc/DropFirewallRule()
if (!enabled)
return
active_bans = list()
. = shell("netsh advfirewall firewall delete rule name=\"[rule_name]\"")
if (.)
subsystem_log("Failed to drop firewall rule. Exit code: [.].")
else if (isnull(.))
subsystem_log("Failed to invoke shell for firewall rule drop.")
else
subsystem_log("Firewall rule dropped.")
+9 -2
View File
@@ -221,6 +221,12 @@ SUBSYSTEM_DEF(ticker)
var/init_start = world.timeofday
//Create and announce mode
var/list/datum/game_mode/runnable_modes
if(SSvote.mode && (SSvote.mode == "roundtype" || SSvote.mode == "dynamic" || SSvote.mode == "mode tiers"))
SSvote.result()
SSpersistence.SaveSavedVotes()
for(var/client/C in SSvote.voting)
C << browse(null, "window=vote;can_close=0")
SSvote.reset()
if(GLOB.master_mode == "random" || GLOB.master_mode == "secret")
runnable_modes = config.get_runnable_modes()
@@ -484,9 +490,10 @@ SUBSYSTEM_DEF(ticker)
SSticker.modevoted = TRUE
var/dynamic = CONFIG_GET(flag/dynamic_voting)
if(dynamic)
SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 2 MINUTES)
SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 20 MINUTES)
else
SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, vote_time = 2 MINUTES)
SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, \
vote_time = (CONFIG_GET(flag/modetier_voting) ? 1 MINUTES : 20 MINUTES))
/datum/controller/subsystem/ticker/Recover()
current_state = SSticker.current_state
+39 -3
View File
@@ -15,6 +15,7 @@ SUBSYSTEM_DEF(vote)
var/vote_system = PLURALITY_VOTING
var/question = null
var/list/choices = list()
var/list/scores = list()
var/list/choice_descs = list() // optional descriptions
var/list/voted = list()
var/list/voting = list()
@@ -26,6 +27,8 @@ SUBSYSTEM_DEF(vote)
var/list/stored_gamemode_votes = list() //Basically the last voted gamemode is stored here for end-of-round use.
var/list/stored_modetier_results = list() // The aggregated tier list of the modes available in secret.
/datum/controller/subsystem/vote/fire() //called by master_controller
if(mode)
if(end_time < world.time)
@@ -33,7 +36,8 @@ SUBSYSTEM_DEF(vote)
SSpersistence.SaveSavedVotes()
for(var/client/C in voting)
C << browse(null, "window=vote;can_close=0")
reset()
if(end_time < world.time) // result() can change this
reset()
else if(next_pop < world.time)
var/datum/browser/client_popup
for(var/client/C in voting)
@@ -54,6 +58,7 @@ SUBSYSTEM_DEF(vote)
choice_descs.Cut()
voted.Cut()
voting.Cut()
scores.Cut()
obfuscated = FALSE //CIT CHANGE - obfuscated votes
remove_action_buttons()
@@ -116,10 +121,8 @@ SUBSYSTEM_DEF(vote)
var/opposite_pref = d[j][i]
if(pref_number>opposite_pref)
p[i][j] = d[i][j]
p[j][i] = 0
else
p[i][j] = 0
p[j][i] = d[i][j]
for(var/i in 1 to choices.len)
for(var/j in 1 to choices.len)
if(i != j)
@@ -180,6 +183,22 @@ SUBSYSTEM_DEF(vote)
score.Cut(median_pos,median_pos+1)
choices[score_name]++
/datum/controller/subsystem/vote/proc/calculate_scores(var/blackbox_text)
var/list/scores_by_choice = list()
for(var/choice in choices)
scores_by_choice[choice] = list()
for(var/ckey in voted)
var/list/this_vote = voted[ckey]
for(var/choice in this_vote)
sorted_insert(scores_by_choice[choice],this_vote[choice],/proc/cmp_numeric_asc)
var/middle_score = round(GLOB.vote_score_options.len/2,1)
for(var/score_name in scores_by_choice)
var/list/score = scores_by_choice[score_name]
for(var/S in score)
scores[score_name] += S-middle_score
SSblackbox.record_feedback("nested tally","voting",scores[score_name],list(blackbox_text,"Total scores",score_name))
/datum/controller/subsystem/vote/proc/announce_result()
var/vote_title_text
var/text
@@ -234,6 +253,8 @@ SUBSYSTEM_DEF(vote)
var/admintext = "Obfuscated results"
if(vote_system == RANKED_CHOICE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
else if(vote_system == SCORE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!"
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
admintext += "\n<b>[choices[i]]:</b> [votes]"
@@ -252,6 +273,12 @@ SUBSYSTEM_DEF(vote)
SSticker.save_mode(.)
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
if(CONFIG_GET(flag/modetier_voting))
reset()
started_time = 0
initiate_vote("mode tiers","server",hideresults=FALSE,votesystem=RANKED_CHOICE_VOTING,forced=TRUE, vote_time = 30 MINUTES)
to_chat(world,"<b>The vote will end right as the round starts.</b>")
return .
if("restart")
if(. == "Restart Round")
restart = 1
@@ -262,6 +289,8 @@ SUBSYSTEM_DEF(vote)
restart = 1
else
GLOB.master_mode = .
if("mode tiers")
stored_modetier_results = choices.Copy()
if("dynamic")
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
@@ -382,6 +411,13 @@ SUBSYSTEM_DEF(vote)
choices |= M
if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote
choices.Add("secret", "extended")
if("mode tiers")
var/list/modes_to_add = config.votable_modes
var/list/probabilities = CONFIG_GET(keyed_list/probability)
for(var/tag in modes_to_add)
if(probabilities[tag] <= 0)
modes_to_add -= tag
choices.Add(modes_to_add)
if("dynamic")
for(var/T in config.storyteller_cache)
var/datum/dynamic_storyteller/S = T
+60
View File
@@ -0,0 +1,60 @@
/datum/element/wuv //D'awwwww
element_flags = ELEMENT_BESPOKE
id_arg_index = 2
//the for the me emote proc call when petted.
var/pet_emote
//whether the emote is visible or audible
var/pet_type
//same as above, except when harmed. "You are going into orbit, you stupid mutt!"
var/punt_emote
//same as pet_type
var/punt_type
//mood typepath for the moodlet signal when petted.
var/pet_moodlet
//same as above but for harm
var/punt_moodlet
/datum/element/wuv/Attach(datum/target, pet, pet_t, pet_mood, punt, punt_t, punt_mood)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
pet_emote = pet
pet_type = pet_t
punt_emote = punt
punt_type = punt_t
pet_moodlet = pet_mood
punt_moodlet = punt_mood
RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
var/mob/living/L = source
if(L.stat == DEAD)
return
//we want to delay the effect to be displayed after the mob is petted, not before.
switch(user.a_intent)
if(INTENT_HARM, INTENT_DISARM)
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
if(INTENT_HELP)
addtimer(CALLBACK(src, .proc/pet_the_dog, source, user), 1)
/datum/element/wuv/proc/pet_the_dog(mob/target, mob/user)
if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
return
new /obj/effect/temp_visual/heart(target.loc)
if(pet_emote)
target.emote("me", pet_type, pet_emote)
if(pet_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1)) //prevents unlimited happiness petting park exploit.
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, pet_moodlet, target)
/datum/element/wuv/proc/kick_the_dog(mob/target, mob/user)
if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
return
if(punt_emote)
target.emote("me", punt_type, punt_emote)
if(punt_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, punt_moodlet, target)
+1 -1
View File
@@ -525,7 +525,7 @@
if(!objective)
to_chat(usr,"Invalid objective.")
return
//qdel(objective) Needs cleaning objective destroys
qdel(objective) //TODO: Needs cleaning objective destroys (whatever that means)
message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]")
log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]")
@@ -23,10 +23,13 @@
mood_change = 3
timeout = 3000
/datum/mood_event/pet_corgi
description = "<span class='nicegreen'>Corgis are adorable! I can't stop petting them!</span>\n"
mood_change = 3
timeout = 3000
/datum/mood_event/pet_animal
description = "<span class='nicegreen'>Animals are adorable! I can't stop petting them!</span>\n"
mood_change = 2
timeout = 5 MINUTES
/datum/mood_event/pet_animal/add_effects(mob/animal)
description = "<span class='nicegreen'>\The [animal.name] is adorable! I can't stop petting [animal.p_them()]!</span>\n"
/datum/mood_event/honk
description = "<span class='nicegreen'>Maybe clowns aren't so bad after all. Honk!</span>\n"
+6
View File
@@ -99,6 +99,12 @@
id = "Mesmerize"
alert_type = /obj/screen/alert/status_effect/mesmerized
/datum/status_effect/no_combat_mode/mesmerize/on_apply()
ADD_TRAIT(owner, TRAIT_MUTE, "mesmerize")
/datum/status_effect/no_combat_mode/mesmerize/on_remove()
REMOVE_TRAIT(owner, TRAIT_MUTE, "mesmerize")
/obj/screen/alert/status_effect/mesmerized
name = "Mesmerized"
desc = "You cant tear your sight from who is in front of you...Their gaze is simply too enthralling.."
+9
View File
@@ -122,6 +122,15 @@
gain_text = "<span class='notice'>You feel like exposing yourself to the world.</span>"
lose_text = "<span class='notice'>Indecent exposure doesn't sound as charming to you anymore.</span>"
/datum/quirk/coldblooded
name = "Cold-blooded"
desc = "Your body doesn't create its own internal heat, requiring external heat regulation."
value = 0
medical_record_text = "Patient is ectothermic."
mob_trait = TRAIT_COLDBLOODED
gain_text = "<span class='notice'>You feel cold-blooded.</span>"
lose_text = "<span class='notice'>You feel more warm-blooded.</span>"
/datum/quirk/alcohol_intolerance
name = "Alcohol Intolerance"
desc = "You take toxin damage from alcohol rather than getting drunk."
+15 -15
View File
@@ -25,16 +25,16 @@
var/key_valid
var/require_comms_key = FALSE
/datum/world_topic/proc/TryRun(list/input)
/datum/world_topic/proc/TryRun(list/input, addr)
key_valid = config && (CONFIG_GET(string/comms_key) == input["key"])
if(require_comms_key && !key_valid)
return "Bad Key"
input -= "key"
. = Run(input)
. = Run(input, addr)
if(islist(.))
. = list2params(.)
/datum/world_topic/proc/Run(list/input)
/datum/world_topic/proc/Run(list/input, addr)
CRASH("Run() not implemented for [type]!")
// TOPICS
@@ -43,7 +43,7 @@
keyword = "ping"
log = FALSE
/datum/world_topic/ping/Run(list/input)
/datum/world_topic/ping/Run(list/input, addr)
. = 0
for (var/client/C in GLOB.clients)
++.
@@ -52,7 +52,7 @@
keyword = "playing"
log = FALSE
/datum/world_topic/playing/Run(list/input)
/datum/world_topic/playing/Run(list/input, addr)
return GLOB.player_list.len
/datum/world_topic/pr_announce
@@ -60,7 +60,7 @@
require_comms_key = TRUE
var/static/list/PRcounts = list() //PR id -> number of times announced this round
/datum/world_topic/pr_announce/Run(list/input)
/datum/world_topic/pr_announce/Run(list/input, addr)
var/list/payload = json_decode(input["payload"])
var/id = "[payload["pull_request"]["id"]]"
if(!PRcounts[id])
@@ -78,14 +78,14 @@
keyword = "Ahelp"
require_comms_key = TRUE
/datum/world_topic/ahelp_relay/Run(list/input)
/datum/world_topic/ahelp_relay/Run(list/input, addr)
relay_msg_admins("<span class='adminnotice'><b><font color=red>HELP: </font> [input["source"]] [input["message_sender"]]: [input["message"]]</b></span>")
/datum/world_topic/comms_console
keyword = "Comms_Console"
require_comms_key = TRUE
/datum/world_topic/comms_console/Run(list/input)
/datum/world_topic/comms_console/Run(list/input, addr)
minor_announce(input["message"], "Incoming message from [input["message_sender"]]")
for(var/obj/machinery/computer/communications/CM in GLOB.machines)
CM.overrideCooldown()
@@ -94,17 +94,17 @@
keyword = "News_Report"
require_comms_key = TRUE
/datum/world_topic/news_report/Run(list/input)
/datum/world_topic/news_report/Run(list/input, addr)
minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")
/datum/world_topic/server_hop
keyword = "server_hop"
/datum/world_topic/server_hop/Run(list/input)
/datum/world_topic/server_hop/Run(list/input, addr)
var/expected_key = input[keyword]
for(var/mob/dead/observer/O in GLOB.player_list)
if(O.key == expected_key)
if(O.client)
if(O.client?.address == addr)
new /obj/screen/splash(O.client, TRUE)
break
@@ -112,14 +112,14 @@
keyword = "adminmsg"
require_comms_key = TRUE
/datum/world_topic/adminmsg/Run(list/input)
/datum/world_topic/adminmsg/Run(list/input, addr)
return IrcPm(input[keyword], input["msg"], input["sender"])
/datum/world_topic/namecheck
keyword = "namecheck"
require_comms_key = TRUE
/datum/world_topic/namecheck/Run(list/input)
/datum/world_topic/namecheck/Run(list/input, addr)
//Oh this is a hack, someone refactor the functionality out of the chat command PLS
var/datum/tgs_chat_command/namecheck/NC = new
var/datum/tgs_chat_user/user = new
@@ -131,13 +131,13 @@
keyword = "adminwho"
require_comms_key = TRUE
/datum/world_topic/adminwho/Run(list/input)
/datum/world_topic/adminwho/Run(list/input, addr)
return ircadminwho()
/datum/world_topic/status
keyword = "status"
/datum/world_topic/status/Run(list/input)
/datum/world_topic/status/Run(list/input, addr)
. = list()
.["version"] = GLOB.game_version
.["mode"] = "hidden" //CIT CHANGE - hides the gamemode in topic() calls to prevent meta'ing the gamemode
@@ -9,6 +9,8 @@
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
// The antags you're allowed to be if turning Vassal.
/proc/isvamp(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/bloodsucker)
/datum/game_mode/bloodsucker
name = "bloodsucker"
+1 -1
View File
@@ -14,7 +14,7 @@
circuit = /obj/item/circuitboard/machine/sleeper
req_access = list(ACCESS_CMO) //Used for reagent deletion and addition of non medicines
var/efficiency = 1
var/min_health = -25
var/min_health = 30
var/list/available_chems
var/controls_inside = FALSE
var/list/possible_chems = list(
+2
View File
@@ -155,6 +155,8 @@
mess = TRUE
update_icon()
return FALSE
if(isvamp(clonemind)) //If the mind is a bloodsucker
return FALSE
attempting = TRUE //One at a time!!
countdown.start()
+46 -7
View File
@@ -7,26 +7,51 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
icon_state = "tdoppler"
density = TRUE
var/integrated = FALSE
var/list_limit = 100
var/cooldown = 10
var/next_announce = 0
var/max_dist = 150
verb_say = "states coldly"
var/list/message_log = list()
/obj/machinery/doppler_array/Initialize()
. = ..()
GLOB.doppler_arrays += src
/obj/machinery/doppler_array/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE,null,null,CALLBACK(src,.proc/rot_message))
/obj/machinery/doppler_array/Destroy()
GLOB.doppler_arrays -= src
return ..()
/obj/machinery/doppler_array/examine(mob/user)
/obj/machinery/doppler_array/ui_interact(mob/user)
. = ..()
. += "<span class='notice'>Its dish is facing to the [dir2text(dir)].</span>"
if(stat)
return FALSE
/obj/machinery/doppler_array/process()
return PROCESS_KILL
var/list/dat = list()
for(var/i in 1 to LAZYLEN(message_log))
dat += "Log recording #[i]: [message_log[i]]<br/><br>"
dat += "<A href='?src=[REF(src)];delete_log=1'>Delete logs</A><br>"
dat += "<hr>"
dat += "<A href='?src=[REF(src)];refresh=1'>(Refresh)</A><br>"
dat += "</body></html>"
var/datum/browser/popup = new(user, "computer", name, 400, 500)
popup.set_content(dat.Join(" "))
popup.open()
/obj/machinery/doppler_array/Topic(href, href_list)
if(..())
return
if(href_list["delete_log"])
LAZYCLEARLIST(message_log)
if(href_list["refresh"])
updateUsrDialog()
updateUsrDialog()
return
/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wrench))
@@ -46,15 +71,18 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
to_chat(user, "<span class='notice'>You adjust [src]'s dish to face to the [dir2text(dir)].</span>")
playsound(src, 'sound/items/screwdriver2.ogg', 50, 1)
/obj/machinery/doppler_array/proc/sense_explosion(turf/epicenter,devastation_range,heavy_impact_range,light_impact_range,
took,orig_dev_range,orig_heavy_range,orig_light_range)
/obj/machinery/doppler_array/proc/sense_explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range,
took, orig_dev_range, orig_heavy_range, orig_light_range)
if(stat & NOPOWER)
return FALSE
var/turf/zone = get_turf(src)
if(zone.z != epicenter.z)
return FALSE
if(next_announce > world.time)
return FALSE
next_announce = world.time + cooldown
var/distance = get_dist(epicenter, zone)
var/direct = get_dir(zone, epicenter)
@@ -80,8 +108,19 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
else
for(var/message in messages)
say(message)
if(LAZYLEN(message_log) > list_limit)
say("Storage buffer is full! Clearing buffers...")
LAZYCLEARLIST(message_log)
LAZYADD(message_log, messages.Join(" "))
return TRUE
/obj/machinery/doppler_array/examine(mob/user)
. = ..()
. += "<span class='notice'>Its dish is facing to the [dir2text(dir)].</span>"
/obj/machinery/doppler_array/process()
return PROCESS_KILL
/obj/machinery/doppler_array/power_change()
if(stat & BROKEN)
icon_state = "[initial(icon_state)]-broken"
-1
View File
@@ -256,7 +256,6 @@
return
return ..()
/obj/machinery/firealarm/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(.) //damage received
+22
View File
@@ -18,6 +18,28 @@
. = ..()
update_icon()
/obj/machinery/recharge_station/upgraded
/obj/machinery/recharge_station/upgraded/Initialize()
. = ..()
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/cyborgrecharger(null)
component_parts += new /obj/item/stock_parts/capacitor/super(null)
component_parts += new /obj/item/stock_parts/manipulator/pico(null)
component_parts += new /obj/item/stock_parts/cell/hyper(null)
RefreshParts()
/obj/machinery/recharge_station/fullupgrade
/obj/machinery/recharge_station/fullupgrade/Initialize()
. = ..()
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/cyborgrecharger(null)
component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
component_parts += new /obj/item/stock_parts/manipulator/femto(null)
component_parts += new /obj/item/stock_parts/cell/bluespace(null)
RefreshParts()
/obj/machinery/recharge_station/RefreshParts()
recharge_speed = 0
repairs = 0
+10 -10
View File
@@ -700,10 +700,10 @@ GLOBAL_LIST_EMPTY(PDAs)
U << browse(null, "window=pda")
return
/obj/item/pda/proc/remove_id()
if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
/obj/item/pda/proc/remove_id(mob/user)
if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
do_remove_id(usr)
do_remove_id(user)
/obj/item/pda/proc/do_remove_id(mob/user)
if(!id)
@@ -827,23 +827,23 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/proc/create_message(mob/living/U, obj/item/pda/P)
send_message(U,list(P))
/obj/item/pda/AltClick()
/obj/item/pda/AltClick(mob/user)
. = ..()
if(id)
remove_id()
remove_id(user)
playsound(src, 'sound/machines/terminal_eject_disc.ogg', 50, 1)
else
remove_pen()
remove_pen(user)
playsound(src, 'sound/machines/button4.ogg', 50, 1)
return TRUE
/obj/item/pda/CtrlClick()
/obj/item/pda/CtrlClick(mob/user)
..()
if(isturf(loc)) //stops the user from dragging the PDA by ctrl-clicking it.
return
remove_pen()
remove_pen(user)
/obj/item/pda/verb/verb_toggle_light()
set category = "Object"
@@ -857,7 +857,7 @@ GLOBAL_LIST_EMPTY(PDAs)
set src in usr
if(id)
remove_id()
remove_id(usr)
else
to_chat(usr, "<span class='warning'>This PDA does not have an ID in it!</span>")
@@ -896,7 +896,7 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/proc/id_check(mob/user, obj/item/card/id/I)
if(!I)
if(id && (src in user.contents))
remove_id()
remove_id(user)
return TRUE
else
var/obj/item/card/id/C = user.get_active_held_item()
@@ -814,3 +814,93 @@
return
else
to_chat(user, "<span class='danger'>Your gripper cannot hold \the [target].</span>")
/obj/item/weapon/gripper/mining
name = "shelter capsule deployer"
desc = "A simple grasping tool for carrying and deploying shelter capsules."
icon_state = "gripper_mining"
can_hold = list(
/obj/item/survivalcapsule
)
/obj/item/weapon/gripper/mining/attack_self()
if(wrapped)
wrapped.forceMove(get_turf(wrapped))
wrapped.attack_self()
wrapped = null
return
/obj/item/gun/energy/plasmacutter/cyborg
name = "cyborg plasma cutter"
desc = "A basic variation of the plasma cutter, compressed into a cyborg chassis. Less effective than normal plasma cutters."
force = 15
ammo_type = list(/obj/item/ammo_casing/energy/plasma/weak)
can_charge = FALSE
selfcharge = EGUN_SELFCHARGE_BORG
cell_type = /obj/item/stock_parts/cell/secborg
charge_delay = 5
/obj/item/cyborg_clamp
name = "cyborg loading clamp"
desc = "Equipment for supply cyborgs. Lifts objects and loads them into cargo. Will not carry living beings."
icon = 'icons/mecha/mecha_equipment.dmi'
icon_state = "mecha_clamp"
tool_behaviour = TOOL_RETRACTOR
item_flags = NOBLUDGEON
flags_1 = NONE
var/cargo_capacity = 8
var/cargo = list()
/obj/item/cyborg_clamp/attack(mob/M, mob/user, def_zone)
return
/obj/item/cyborg_clamp/afterattack(atom/movable/target, mob/user, proximity)
. = ..()
if(!proximity)
return FALSE
if(isobj(target))
var/obj/O = target
if(!O.anchored)
if(contents.len < cargo_capacity)
user.visible_message("[user] lifts [target] and starts to load it into its cargo compartment.")
O.anchored = TRUE
if(do_mob(user, O, 20))
for(var/mob/chump in target.GetAllContents())
to_chat(user, "<span class='warning'>Error: Living entity detected in [target]. Cannot load.</span>")
O.anchored = initial(O.anchored)
return
for(var/obj/item/disk/nuclear/diskie in target.GetAllContents())
to_chat(user, "<span class='warning'>Error: Nuclear class authorization device detected in [target]. Cannot load.</span>")
O.anchored = initial(O.anchored)
return
if(contents.len < cargo_capacity) //check both before and after
cargo += O
O.forceMove(src)
O.anchored = FALSE
to_chat(user, "<span class='notice'>[target] successfully loaded.</span>")
playsound(loc, 'sound/effects/bin_close.ogg', 50, 0)
else
to_chat(user, "<span class='warning'>Not enough room in cargo compartment! Maximum of [cargo_capacity] objects!</span>")
O.anchored = initial(O.anchored)
return
else
O.anchored = initial(O.anchored)
else
to_chat(user, "<span class='warning'>Not enough room in cargo compartment! Maximum of eight objects!</span>")
else
to_chat(user, "<span class='warning'>[target] is firmly secured!</span>")
/obj/item/cyborg_clamp/attack_self(mob/user)
var/obj/chosen_cargo = input(user, "Drop what?") as null|anything in cargo
if(!chosen_cargo)
return
chosen_cargo.forceMove(get_turf(chosen_cargo))
cargo -= chosen_cargo
user.visible_message("[user] unloads [chosen_cargo] from its cargo.")
playsound(loc, 'sound/effects/bin_close.ogg', 50, 0)
/obj/item/card/id/miningborg
name = "mining point card"
desc = "A robotic ID strip used for claiming and transferring mining points. Must be held in an active slot to transfer points."
access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
icon_state = "data_1"
+47 -15
View File
@@ -173,32 +173,64 @@
R.module.basic_modules += S
R.module.add_module(S, FALSE, TRUE)
/obj/item/borg/upgrade/soh
name = "mining cyborg satchel of holding"
desc = "A satchel of holding replacement for mining cyborg's ore satchel module."
/obj/item/borg/upgrade/premiumka
name = "mining cyborg premium KA"
desc = "A premium kinetic accelerator replacement for the mining module's standard kinetic accelerator."
icon_state = "cyborg_upgrade3"
require_module = 1
module_type = list(/obj/item/robot_module/miner)
/obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R)
/obj/item/borg/upgrade/premiumka/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
for(var/obj/item/storage/bag/ore/cyborg/S in R.module)
R.module.remove_module(S, TRUE)
for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA in R.module)
for(var/obj/item/borg/upgrade/modkit/M in KA.modkits)
M.uninstall(src)
R.module.remove_module(KA, TRUE)
var/obj/item/storage/bag/ore/holding/H = new /obj/item/storage/bag/ore/holding(R.module)
R.module.basic_modules += H
R.module.add_module(H, FALSE, TRUE)
var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA = new /obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg(R.module)
R.module.basic_modules += PKA
R.module.add_module(PKA, FALSE, TRUE)
/obj/item/borg/upgrade/soh/deactivate(mob/living/silicon/robot/R, user = usr)
/obj/item/borg/upgrade/premiumka/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
if (.)
for(var/obj/item/storage/bag/ore/holding/H in R.module)
R.module.remove_module(H, TRUE)
for(var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA in R.module)
for(var/obj/item/borg/upgrade/modkit/M in PKA.modkits)
M.uninstall(src)
R.module.remove_module(PKA, TRUE)
var/obj/item/storage/bag/ore/cyborg/S = new (R.module)
R.module.basic_modules += S
R.module.add_module(S, FALSE, TRUE)
var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA = new (R.module)
R.module.basic_modules += KA
R.module.add_module(KA, FALSE, TRUE)
/obj/item/borg/upgrade/advcutter
name = "mining cyborg advanced plasma cutter"
desc = "An upgrade for the mining cyborgs plasma cutter, bringing it to advanced operation."
icon_state = "cyborg_upgrade3"
require_module = 1
module_type = list(/obj/item/robot_module/miner)
/obj/item/borg/upgrade/advcutter/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
for(var/obj/item/gun/energy/plasmacutter/cyborg/C in R.module)
C.name = "advanced cyborg plasma cutter"
C.desc = "An improved version of the cyborg plasma cutter. Baring functionality identical to the standard hand held version."
C.icon_state = "adv_plasmacutter"
for(var/obj/item/ammo_casing/energy/plasma/weak/L in C.ammo_type)
L.projectile_type = /obj/item/projectile/plasma/adv
/obj/item/borg/upgrade/advcutter/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
if (.)
for(var/obj/item/gun/energy/plasmacutter/cyborg/C in R.module)
C.name = initial(name)
C.desc = initial(desc)
C.icon_state = initial(icon_state)
for(var/obj/item/ammo_casing/energy/plasma/weak/L in C.ammo_type)
L.projectile_type = initial(L.projectile_type)
/obj/item/borg/upgrade/tboh
name = "janitor cyborg trash bag of holding"
+13 -1
View File
@@ -110,6 +110,7 @@
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
var/mob/listeningTo
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
var/range = null
/obj/item/storage/bag/ore/ComponentInitialize()
. = ..()
@@ -130,7 +131,8 @@
/obj/item/storage/bag/ore/dropped()
. = ..()
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
listeningTo = null
/obj/item/storage/bag/ore/proc/Pickup_ores(mob/living/user)
@@ -141,12 +143,21 @@
return
if (istype(user.pulling, /obj/structure/ore_box))
box = user.pulling
if(issilicon(user))
var/mob/living/silicon/robot/borgo = user
for(var/obj/item/cyborg_clamp/C in borgo.module.modules)
for(var/obj/structure/ore_box/B in C)
box = B
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
if(STR)
for(var/A in tile)
if (!is_type_in_typecache(A, STR.can_hold))
continue
if (box)
if(range)
for(var/obj/item/stack/ore/ore in range(range, user))
user.transferItemToLoc(ore, box)
user.transferItemToLoc(A, box)
show_message = TRUE
else if(SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, A, user, TRUE))
@@ -168,6 +179,7 @@
/obj/item/storage/bag/ore/cyborg
name = "cyborg mining satchel"
range = 1
/obj/item/storage/bag/ore/cyborg/ComponentInitialize()
. = ..()
@@ -88,6 +88,28 @@
else
return ..()
/obj/structure/chair/alt_attack_hand(mob/living/user)
if(Adjacent(user) && istype(user))
if(!item_chair || !user.can_hold_items() || !has_buckled_mobs() || buckled_mobs.len > 1 || dir != user.dir || flags_1 & NODECONSTRUCT_1)
return TRUE
if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return TRUE
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")
return TRUE
var/mob/living/poordude = buckled_mobs[1]
if(!istype(poordude))
return TRUE
user.visible_message("<span class='notice'>[user] pulls [src] out from under [poordude].</span>", "<span class='notice'>You pull [src] out from under [poordude].</span>")
var/C = new item_chair(loc)
user.put_in_hands(C)
poordude.Knockdown(20)//rip in peace
user.adjustStaminaLoss(5)
unbuckle_all_mobs(TRUE)
qdel(src)
return TRUE
/obj/structure/chair/attack_tk(mob/user)
if(!anchored || has_buckled_mobs() || !isturf(user.loc))
..()
@@ -22,7 +22,9 @@
if(opened || move_delay || user.stat || user.IsStun() || user.IsKnockdown() || user.IsUnconscious() || !isturf(loc) || !has_gravity(loc))
return
move_delay = TRUE
if(step(src, direction))
var/oldloc = loc
step(src, direction)
if(oldloc != loc)
addtimer(CALLBACK(src, .proc/ResetMoveDelay), (use_mob_movespeed ? user.movement_delay() : CONFIG_GET(number/movedelay/walk_delay)) * move_speed_multiplier)
else
ResetMoveDelay()
@@ -237,6 +237,12 @@
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
req_access = list(ACCESS_RD)
/obj/structure/displaycase/clown
desc = "In the event of clown, honk glass."
alert = TRUE
start_showpiece_type = /obj/item/bikehorn
req_access = list(ACCESS_CENT_GENERAL)
/obj/structure/displaycase/trophy
name = "trophy display case"
desc = "Store your trophies of accomplishment in here, and they will stay forever."
+1 -1
View File
@@ -84,7 +84,7 @@
/obj/structure/grille/attack_animal(mob/user)
. = ..()
if(!shock(user, 70))
if(!shock(user, 70) && !QDELETED(src)) //Last hit still shocks but shouldn't deal damage to the grille)
take_damage(rand(5,10), BRUTE, "melee", 1)
/obj/structure/grille/attack_paw(mob/user)
+1 -1
View File
@@ -18,7 +18,7 @@
// flags = CONDUCT_1
/obj/structure/lattice/examine(mob/user)
..()
. = ..()
. += deconstruction_hints(user)
/obj/structure/lattice/proc/deconstruction_hints(mob/user)
@@ -182,6 +182,17 @@
else
return ..()
/obj/structure/table/alt_attack_hand(mob/user)
if(user && Adjacent(user) && !user.incapacitated())
user.setClickCooldown(CLICK_CD_MELEE)
if(istype(user) && user.a_intent == INTENT_HARM)
user.visible_message("<span class='warning'>[user] slams [user.p_their()] palms down on [src].</span>", "<span class='warning'>You slam your palms down on [src].</span>")
playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1)
else
user.visible_message("<span class='notice'>[user] slaps [user.p_their()] hands on [src].</span>", "<span class='notice'>You slap your hands on [src].</span>")
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
user.do_attack_animation(src)
return TRUE
/obj/structure/table/deconstruct(disassembled = TRUE, wrench_disassembly = 0)
if(!(flags_1 & NODECONSTRUCT_1))
+14 -18
View File
@@ -177,7 +177,7 @@
I.play_tool_sound(src, 80)
return remove_tile(user, silent)
/turf/open/floor/proc/remove_tile(mob/user, silent = FALSE, make_tile = TRUE)
/turf/open/floor/proc/remove_tile(mob/user, silent = FALSE, make_tile = TRUE, forced = FALSE)
if(broken || burnt)
broken = 0
burnt = 0
@@ -191,24 +191,20 @@
return make_plating()
/turf/open/floor/singularity_pull(S, current_size)
..()
if(current_size == STAGE_THREE)
if(prob(30))
. = ..()
switch(current_size)
if(STAGE_THREE)
if(floor_tile && prob(30))
remove_tile()
if(STAGE_FOUR)
if(floor_tile && prob(50))
remove_tile()
if(STAGE_FIVE to INFINITY)
if(floor_tile)
new floor_tile(src)
make_plating()
else if(current_size == STAGE_FOUR)
if(prob(50))
if(floor_tile)
new floor_tile(src)
make_plating()
else if(current_size >= STAGE_FIVE)
if(floor_tile)
if(prob(70))
new floor_tile(src)
make_plating()
else if(prob(50))
ReplaceWithLattice()
if(prob(70))
remove_tile()
else if(prob(50))
ReplaceWithLattice()
/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
. = ..()
@@ -44,7 +44,7 @@
C.play_tool_sound(src, 80)
return remove_tile(user, silent, (C.tool_behaviour == TOOL_SCREWDRIVER))
/turf/open/floor/wood/remove_tile(mob/user, silent = FALSE, make_tile = TRUE)
/turf/open/floor/wood/remove_tile(mob/user, silent = FALSE, make_tile = TRUE, forced = FALSE)
if(broken || burnt)
broken = 0
burnt = 0
@@ -76,14 +76,16 @@
/turf/open/floor/engine/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FIVE)
if(current_size >= STAGE_FIVE && prob(30))
if(floor_tile)
if(prob(30))
new floor_tile(src)
make_plating()
else if(prob(30))
remove_tile(forced = TRUE)
else
ReplaceWithLattice()
/turf/open/floor/engine/remove_tile(mob/user, silent = FALSE, make_tile = TRUE, forced = FALSE)
if(forced)
return ..()
/turf/open/floor/engine/attack_paw(mob/user)
return attack_hand(user)
+7 -1
View File
@@ -47,7 +47,8 @@
return ..()
/turf/closed/mineral/attackby(obj/item/I, mob/user, params)
/turf/closed/mineral/attackby(obj/item/pickaxe/I, mob/user, params)
var/stored_dir = user.dir
if (!user.IsAdvancedToolUser())
to_chat(usr, "<span class='warning'>You don't have the dexterity to do this!</span>")
return
@@ -63,7 +64,12 @@
to_chat(user, "<span class='notice'>You start picking...</span>")
if(I.use_tool(src, user, 40, volume=50))
var/range = I.digrange //Store the current digrange so people don't cheese digspeed swapping for faster mining
if(ismineralturf(src))
if(I.digrange > 0)
for(var/turf/closed/mineral/M in range(user,range))
if(get_dir(user,M)&stored_dir)
M.gets_drilled()
to_chat(user, "<span class='notice'>You finish cutting into the rock.</span>")
gets_drilled(user)
SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type)
+11 -1
View File
@@ -112,6 +112,7 @@ GLOBAL_VAR(restart_counter)
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"
GLOB.subsystem_log = "[GLOB.log_directory]/subsystem.log"
#ifdef UNIT_TESTS
GLOB.test_log = file("[GLOB.log_directory]/tests.log")
@@ -126,6 +127,7 @@ GLOBAL_VAR(restart_counter)
start_log(GLOB.world_qdel_log)
start_log(GLOB.world_runtime_log)
start_log(GLOB.world_job_debug_log)
start_log(GLOB.subsystem_log)
GLOB.changelog_hash = md5('html/changelog.html') //for telling if the changelog has changed recently
if(fexists(GLOB.config_error_log))
@@ -143,6 +145,14 @@ GLOBAL_VAR(restart_counter)
/world/Topic(T, addr, master, key)
TGS_TOPIC //redirect to server tools if necessary
if(!SSfail2topic)
return "Server not initialized."
else if(SSfail2topic.IsRateLimited(addr))
return "Rate limited."
if(length(T) > CONFIG_GET(number/topic_max_size))
return "Payload too large!"
var/static/list/topic_handlers = TopicHandlers()
var/list/input = params2list(T)
@@ -159,7 +169,7 @@ GLOBAL_VAR(restart_counter)
return
handler = new handler()
return handler.TryRun(input)
return handler.TryRun(input, addr)
/world/proc/AnnouncePR(announcement, list/payload)
var/static/list/PRcounts = list() //PR id -> number of times announced this round
@@ -84,8 +84,8 @@
//It is called from your coffin on close (by you only)
if(poweron_masquerade == TRUE || owner.current.AmStaked())
return FALSE
owner.current.adjustStaminaLoss(-2 + (regenRate * -10) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-1 * (regenRate * 4) * mult, 0)
owner.current.adjustStaminaLoss(-2 + (regenRate * 8) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-0.1 * (regenRate * 2) * mult, 0)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (regenRate * 4) * mult) //adjustBrainLoss(-1 * (regenRate * 4) * mult, 0)
// No Bleeding
if(ishuman(owner.current)) //NOTE Current bleeding is horrible, not to count the amount of blood ballistics delete.
@@ -97,7 +97,7 @@
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_DEATHCOMA)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(amInCoffinWhileTorpor)
mult *= 5 // Increase multiplier if we're sleeping in a coffin.
mult *= 4 // Increase multiplier if we're sleeping in a coffin.
fireheal = min(C.getFireLoss_nonProsthetic(), regenRate) // NOTE: Burn damage ONLY heals in torpor.
costMult = 0.25
C.ExtinguishMob()
@@ -118,6 +118,8 @@
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
if(mult == 0)
return TRUE
if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration while unconcious, so you dont have to wait all day
mult *= 2
// We have damage. Let's heal (one time)
C.adjustBruteLoss(-bruteheal * mult, forced = TRUE)// Heal BRUTE / BURN in random portions throughout the body.
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
@@ -187,19 +189,19 @@
/datum/antagonist/bloodsucker/proc/HandleDeath()
// FINAL DEATH
// Fire Damage? (above double health)
if (owner.current.getFireLoss_nonProsthetic() >= owner.current.getMaxHealth() * 2)
if(owner.current.getFireLoss_nonProsthetic() >= owner.current.getMaxHealth() * 1.5)
FinalDeath()
return
// Staked while "Temp Death" or Asleep
if (owner.current.StakeCanKillMe() && owner.current.AmStaked())
if(owner.current.StakeCanKillMe() && owner.current.AmStaked())
FinalDeath()
return
// Not "Alive"?
if (!owner.current || !isliving(owner.current) || isbrain(owner.current) || !get_turf(owner.current))
if(!owner.current || !isliving(owner.current) || isbrain(owner.current) || !get_turf(owner.current))
FinalDeath()
return
// Missing Brain or Heart?
if (!owner.current.HaveBloodsuckerBodyparts())
if(!owner.current.HaveBloodsuckerBodyparts())
FinalDeath()
return
// Disable Powers: Masquerade * NOTE * This should happen as a FLAW!
@@ -212,21 +214,21 @@
var/total_toxloss = owner.current.getToxLoss() //This is neater than just putting it in total_damage
var/total_damage = total_brute + total_burn + total_toxloss
// Died? Convert to Torpor (fake death)
if (owner.current.stat >= DEAD)
if(owner.current.stat >= DEAD)
Torpor_Begin()
to_chat(owner, "<span class='danger'>Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.</span>")
sleep(30) //To avoid spam
if (poweron_masquerade == TRUE)
to_chat(owner, "<span class='warning'>Your wounds will not heal until you disable the <span class='boldnotice'>Masquerade</span> power.</span>")
// End Torpor:
else // No damage, OR toxin healed AND brute healed and NOT in coffin (since you cannot heal burn)
if (total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
if(total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
// Not Daytime, Not in Torpor
if (!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
Torpor_End()
// Fake Unconscious
if (poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
if(poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
owner.current.Unconscious(20,1)
//HEALTH_THRESHOLD_CRIT 0
//HEALTH_THRESHOLD_FULLCRIT -30
//HEALTH_THRESHOLD_DEAD -100
@@ -241,8 +243,8 @@
owner.current.update_sight()
owner.current.reload_fullscreen()
// Disable ALL Powers
for (var/datum/action/bloodsucker/power in powers)
if (power.active && !power.can_use_in_torpor)
for(var/datum/action/bloodsucker/power in powers)
if(power.active && !power.can_use_in_torpor)
power.DeactivatePower()
@@ -281,7 +283,7 @@
// Free my Vassals!
FreeAllVassals()
// Elders get Dusted
if (vamplevel >= 4) // (vamptitle)
if(vamplevel >= 4) // (vamptitle)
owner.current.visible_message("<span class='warning'>[owner.current]'s skin crackles and dries, their skin and bones withering to dust. A hollow cry whips from what is now a sandy pile of remains.</span>", \
"<span class='userdanger'>Your soul escapes your withering body as the abyss welcomes you to your Final Death.</span>", \
"<span class='italics'>You hear a dry, crackling sound.</span>")
@@ -306,7 +308,7 @@
if (!isliving(src))
return
var/mob/living/L = src
if (!L.AmBloodsucker())
if(!L.AmBloodsucker())
return
// We're a vamp? Try to eat food...
var/datum/antagonist/bloodsucker/bloodsuckerdatum = mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
@@ -315,7 +317,7 @@
/datum/antagonist/bloodsucker/proc/handle_eat_human_food(var/food_nutrition) // Called from snacks.dm and drinks.dm
set waitfor = FALSE
if (!owner.current || !iscarbon(owner.current))
if(!owner.current || !iscarbon(owner.current))
return
var/mob/living/carbon/C = owner.current
// Remove Nutrition, Give Bad Food
@@ -35,8 +35,8 @@
var/warn_sun_burn = FALSE // So we only get the sun burn message once per day.
var/had_toxlover = FALSE
// LISTS
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_VIRUSIMMUNE, TRAIT_NIGHT_VISION, \
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_NOCLONE)
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_NIGHT_VISION, \
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE)
// NOTES: TRAIT_AGEUSIA <-- Doesn't like flavors.
// REMOVED: TRAIT_NODEATH
// TO ADD:
@@ -334,7 +334,7 @@ datum/antagonist/bloodsucker/proc/SpendRank()
// Assign True Reputation
if(vamplevel == 4)
SelectReputation(am_fledgling = FALSE, forced = TRUE)
to_chat(owner.current, "<span class='notice'>You are now a rank [vamplevel] Bloodsucker. Your strength, resistence, health, feed rate, regen rate, and maximum blood have all increased!</span>")
to_chat(owner.current, "<span class='notice'>You are now a rank [vamplevel] Bloodsucker. Your strength, health, feed rate, regen rate, and maximum blood have all increased!</span>")
to_chat(owner.current, "<span class='notice'>Your existing powers have all ranked up as well!</span>")
update_hud(TRUE)
owner.current.playsound_local(null, 'sound/effects/pope_entry.ogg', 25, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
@@ -56,11 +56,6 @@
var/obj/item/organ/eyes/vassal/E = new
E.Insert(owner.current)
/obj/item/organ/eyes/vassal/
lighting_alpha = 180 // LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE <--- This is too low a value at 128. We need to SEE what the darkness is so we can hide in it.
see_in_dark = 12
flash_protect = -1 //These eyes are weaker to flashes, but let you see in the dark
/datum/antagonist/vassal/proc/remove_thrall_eyes()
var/obj/item/organ/eyes/E = new
E.Insert(owner.current)
@@ -51,6 +51,11 @@
return "<span class='danger'>no</span>" // Bloodsuckers don't have a heartbeat at all when stopped (default is "an unstable")
// EYES //
/obj/item/organ/eyes/vassal/
lighting_alpha = 180 // LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE <--- This is too low a value at 128. We need to SEE what the darkness is so we can hide in it.
see_in_dark = 12
flash_protect = -1 //These eyes are weaker to flashes, but let you see in the dark
/obj/item/organ/eyes/vassal/bloodsucker
flash_protect = 2 //Eye healing isnt working properly
sight_flags = SEE_MOBS // Taken from augmented_eyesight.dm
@@ -78,7 +78,7 @@
var/mob/living/carbon/C = target
// Needs to be Down/Slipped in some way to Stake.
if(!C.can_be_staked() || target == user)
to_chat(user, "<span class='danger'>You cant stake [target] when they are moving moving about! They have to be laying down!</span>")
to_chat(user, "<span class='danger'>You can't stake [target] when they are moving about! They have to be laying down or grabbed by the neck!</span>")
return
// Oops! Can't.
if(HAS_TRAIT(C, TRAIT_PIERCEIMMUNE))
@@ -113,7 +113,7 @@
// Can this target be staked? If someone stands up before this is complete, it fails. Best used on someone stationary.
/mob/living/carbon/proc/can_be_staked()
//return resting || IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_FAKEDEATH)) || resting || IsStun() || IsFrozen() || (pulledby && pulledby.grab_state >= GRAB_NECK)
return (src.resting || src.lying)
return (resting || lying || IsUnconscious() || pulledby && pulledby.grab_state >= GRAB_NECK)
// ABOVE: Taken from update_mobility() in living.dm
/obj/item/stake/hardened
@@ -130,7 +130,7 @@
/obj/structure/bloodsucker/vassalrack/MouseDrop_T(atom/movable/O, mob/user)
if(!O.Adjacent(src) || O == user || !isliving(O) || !isliving(user) || useLock || has_buckled_mobs() || user.incapacitated())
return
if(!anchored && user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(!anchored && isvamp(user))
to_chat(user, "<span class='danger'>Until this rack is secured in place, it cannot serve its purpose.</span>")
return
// PULL TARGET: Remember if I was pullin this guy, so we can restore this
@@ -183,7 +183,7 @@
/obj/structure/bloodsucker/vassalrack/user_unbuckle_mob(mob/living/M, mob/user)
// Attempt Unbuckle
if(!user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(!isvamp(user))
if(M == user)
M.visible_message("<span class='danger'>[user] tries to release themself from the rack!</span>",\
"<span class='danger'>You attempt to release yourself from the rack!</span>") // For sound if not seen --> "<span class='italics'>You hear a squishy wet noise.</span>")
@@ -453,7 +453,7 @@
/obj/structure/bloodsucker/candelabrum/examine(mob/user)
. = ..()
if((user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) || isobserver(user))
if((isvamp()) || isobserver(user))
. += {"<span class='cult'>This is a magical candle which drains at the sanity of mortals who are not under your command while it is active.</span>"}
. += {"<span class='cult'>You can alt click on it from any range to turn it on remotely, or simply be next to it and click on it to turn it on and off normally.</span>"}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
@@ -461,15 +461,13 @@
You can turn it on and off by clicking on it while you are next to it</span>"} */
/obj/structure/bloodsucker/candelabrum/attack_hand(mob/user)
var/datum/antagonist/bloodsucker/V = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) //I wish there was a better way to do this
var/datum/antagonist/vassal/T = user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(istype(V) || istype(T))
if(isvamp(user) || istype(T))
toggle()
/obj/structure/bloodsucker/candelabrum/AltClick(mob/user)
var/datum/antagonist/bloodsucker/V = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// Bloodsuckers can turn their candles on from a distance. SPOOOOKY.
if(istype(V))
if(isvamp(user))
toggle()
/obj/structure/bloodsucker/candelabrum/proc/toggle(mob/user)
@@ -486,8 +484,7 @@
if(lit)
for(var/mob/living/carbon/human/H in viewers(7, src))
var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
var/datum/antagonist/bloodsucker/V = H.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(V || T) //We dont want vassals or vampires affected by this
if(isvamp(H) || T) //We dont want vassals or vampires affected by this
return
H.hallucination = 20
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle)
@@ -9,8 +9,9 @@
bloodsucker_can_buy = TRUE
amToggle = TRUE
warn_constant_cost = TRUE
var/was_running
var/light_min = 0.5 // If lum is above this, no good.
var/light_min = 0.2 // If lum is above this, no good.
/datum/action/bloodsucker/cloak/CheckCanUse(display_error)
. = ..()
@@ -26,18 +27,16 @@
/datum/action/bloodsucker/cloak/ActivatePower()
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
var/mob/living/user = owner
var/was_running = (user.m_intent == MOVE_INTENT_RUN)
was_running = (user.m_intent == MOVE_INTENT_RUN)
if(was_running)
user.toggle_move_intent()
ADD_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
// Pay Blood Toll (if awake)
owner.alpha = max(0, owner.alpha - min(75, 20 + 15 * level_current))
owner.alpha = max(20, owner.alpha - min(75, 10 + 5 * level_current))
bloodsuckerdatum.AddBloodVolume(-0.2)
sleep(5) // Check every few ticks that we haven't disabled this power
// Return to Running (if you were before)
if(was_running && user.m_intent != MOVE_INTENT_RUN)
user.toggle_move_intent()
/datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target)
if (!..())
@@ -55,3 +54,5 @@
..()
REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
user.alpha = 255
if(was_running && user.m_intent != MOVE_INTENT_RUN)
user.toggle_move_intent()
@@ -51,14 +51,17 @@
REMOVE_TRAIT(user, TRAIT_COLDBLOODED, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
E.flash_protect = 0
// WE ARE ALIVE! //
bloodsuckerdatum.poweron_masquerade = TRUE
while(bloodsuckerdatum && ContinueActive(user))
// HEART
if (istype(H))
if(istype(H))
H.FakeStart()
// PASSIVE (done from LIFE)
@@ -67,7 +70,7 @@
// Don't Heal
// Pay Blood Toll (if awake)
if (user.stat == CONSCIOUS)
if(user.stat == CONSCIOUS)
bloodsuckerdatum.AddBloodVolume(-0.2)
sleep(20) // Check every few ticks that we haven't disabled this power
@@ -89,9 +92,13 @@
ADD_TRAIT(user, TRAIT_COLDBLOODED, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
// HEART
var/obj/item/organ/heart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
H.Stop()
E.flash_protect = 2
to_chat(user, "<span class='notice'>Your heart beats one final time, while your skin dries out and your icy pallor returns.</span>")
@@ -89,17 +89,14 @@
if(istype(target))
target.Stun(40) //Utterly useless without this, its okay since there are so many checks to go through
target.silent = 45 //Shhhh little lamb
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, 45) //So you cant rotate with combat mode, plus fancy status alert
if(do_mob(user, target, 40, 0, TRUE, extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))
PowerActivatedSuccessfully() // PAY COST! BEGIN COOLDOWN!
var/power_time = 90 + level_current * 12
target.silent = power_time + 20
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, 100 + level_current * 15)
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time + 80)
to_chat(user, "<span class='notice'>[target] is fixed in place by your hypnotic gaze.</span>")
target.Stun(power_time)
//target.silent += power_time / 10 // Silent isn't based on ticks.
target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm
target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
spawn(power_time)
@@ -89,13 +89,7 @@
user.invisibility = INVISIBILITY_MAXIMUM
// LOSE CUFFS
if(user.handcuffed)
var/obj/O = user.handcuffed
user.dropItemToGround(O)
if(user.legcuffed)
var/obj/O = user.legcuffed
user.dropItemToGround(O)
// Wait...
sleep(mist_delay / 2)
@@ -26,6 +26,7 @@
spacewalk = TRUE
sight = SEE_SELF
throwforce = 0
blood_volume = 0
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -182,7 +182,7 @@
destroy_objective.owner = owner
destroy_objective.find_target()
add_objective(destroy_objective)
else if(prob(30) || (mode.storyteller.flags & NO_ASSASSIN))
else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN)))
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = owner
maroon_objective.find_target()
+50
View File
@@ -0,0 +1,50 @@
/obj/item/assembly/playback
name = "playback device"
desc = "A small electronic device able to record a voice sample, and repeat that sample when it receive a signal."
icon_state = "radio"
materials = list(MAT_METAL=500, MAT_GLASS=50)
flags_1 = HEAR_1
attachable = TRUE
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
var/listening = FALSE
var/recorded = "" //the activation message
var/languages
/obj/item/assembly/playback/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(speaker == src)
return
if(listening && !radio_freq)
record_speech(speaker, raw_message, message_language)
/obj/item/assembly/playback/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language)
recorded = raw_message
listening = FALSE
languages = message_language
say("Activation message is '[recorded]'.", language = message_language)
/obj/item/assembly/playback/activate()
if(recorded == "") // Why say anything when there isn't anything to say
return FALSE
say("[recorded]", language = languages) // Repeat the message in the language it was said in
return TRUE
/obj/item/assembly/playback/proc/record()
if(!secured || holder)
return FALSE
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
return TRUE
/obj/item/assembly/playback/attack_self(mob/user)
if(!user)
return FALSE
record()
return TRUE
/obj/item/assembly/playback/toggle_secure()
. = ..()
listening = FALSE
+8 -5
View File
@@ -15,6 +15,7 @@
verb_exclaim = "beeps"
var/listening = FALSE
var/recorded = "" //the activation message
var/languages // The Message's language
var/mode = 1
var/static/list/modes = list("inclusive",
"exclusive",
@@ -33,23 +34,25 @@
if(listening && !radio_freq)
record_speech(speaker, raw_message, message_language)
else
if(check_activation(speaker, raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
if(message_language == languages) // If it isn't in the same language as the message, don't try to find the message
if(check_activation(speaker, raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language)
languages = message_language // Assign the message's language to a variable to use it elsewhere
switch(mode)
if(INCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
say("Activation message is '[recorded]'.", language = languages) // Say the message in the language it was said in
if(EXCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
say("Activation message is '[recorded]'.", language = languages)
if(RECOGNIZER_MODE)
recorded = speaker.GetVoice()
listening = FALSE
say("Your voice pattern is saved.", message_language)
say("Your voice pattern is saved.", language = languages)
if(VOICE_SENSOR_MODE)
if(length(raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
@@ -488,8 +488,7 @@
/obj/item/grenade/clusterbuster/smoke = 15,
/obj/item/clothing/under/chameleon = 13,
/obj/item/clothing/shoes/chameleon/noslip = 10,
/obj/item/borg/upgrade/ddrill = 3,
/obj/item/borg/upgrade/soh = 3)
/obj/item/borg/upgrade/ddrill = 3)
/obj/effect/spawner/lootdrop/snowdin/dungeonmid
name = "dungeon mid"
+1 -1
View File
@@ -5,7 +5,7 @@
department_flag = ENGSEC
faction = "Station"
total_positions = 0
spawn_positions = 1
spawn_positions = 3
supervisors = "your laws and the AI" //Nodrak
selection_color = "#ddffdd"
minimal_player_age = 21
@@ -29,6 +29,15 @@
var/light_on = FALSE
var/brightness_on = 7
/obj/item/twohanded/kinetic_crusher/cyborg //probably give this a unique sprite later
desc = "An integrated version of the standard kinetic crusher with a grinded down axe head to dissuade mis-use against crewmen. Deals damage equal to the standard crusher against creatures, however."
force = 10 //wouldn't want to give a borg a 20 brute melee weapon unemagged now would we
detonation_damage = 60
wielded = 1
/obj/item/twohanded/kinetic_crusher/cyborg/unwield()
return
/obj/item/twohanded/kinetic_crusher/Initialize()
. = ..()
AddComponent(/datum/component/butchering, 60, 110) //technically it's huge and bulky, but this provides an incentive to use it
+20 -3
View File
@@ -16,6 +16,20 @@
toolspeed = 1
usesound = list('sound/effects/picaxe1.ogg', 'sound/effects/picaxe2.ogg', 'sound/effects/picaxe3.ogg')
attack_verb = list("hit", "pierced", "sliced", "attacked")
var/digrange = 1
/obj/item/pickaxe/attack_self(mob/user)
if(initial(digrange) > 0)
if(digrange == 0)
digrange = initial(digrange)
toolspeed = initial(toolspeed)
to_chat(user, "<span class='notice'>You increase the tools dig range, decreasing its mining speed.</span>")
else
digrange = 0
toolspeed = toolspeed/2
to_chat(user, "<span class='notice'>You decrease the tools dig range, increasing its mining speed.</span>")
else
to_chat(user, "<span class='notice'>Tool does not have a configureable dig range.</span>")
/obj/item/pickaxe/suicide_act(mob/living/user)
user.visible_message("<span class='suicide'>[user] begins digging into [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!</span>")
@@ -66,6 +80,7 @@
name = "cyborg mining drill"
desc = "An integrated electric mining drill."
flags_1 = NONE
toolspeed = 0.5
/obj/item/pickaxe/drill/cyborg/Initialize()
. = ..()
@@ -74,23 +89,25 @@
/obj/item/pickaxe/drill/diamonddrill
name = "diamond-tipped mining drill"
icon_state = "diamonddrill"
toolspeed = 0.2
toolspeed = 0.4
desc = "Yours is the drill that will pierce the heavens!"
/obj/item/pickaxe/drill/cyborg/diamond //This is the BORG version!
name = "diamond-tipped cyborg mining drill" //To inherit the NODROP_1 flag, and easier to change borg specific drill mechanics.
icon_state = "diamonddrill"
toolspeed = 0.1
toolspeed = 0.4
digrange = 2
/obj/item/pickaxe/drill/jackhammer
name = "sonic jackhammer"
icon_state = "jackhammer"
item_state = "jackhammer"
w_class = WEIGHT_CLASS_HUGE
toolspeed = 0.1 //the epitome of powertools. extremely fast mining, laughs at puny walls
toolspeed = 0.2 //the epitome of powertools. extremely fast mining, laughs at puny walls
usesound = 'sound/weapons/sonic_jackhammer.ogg'
hitsound = 'sound/weapons/sonic_jackhammer.ogg'
desc = "Cracks rocks with sonic blasts, and doubles as a demolition power tool for smashing walls."
digrange = 2
/obj/item/shovel
name = "shovel"
+147 -144
View File
@@ -1,144 +1,147 @@
/*********************Hivelord stabilizer****************/
/obj/item/hivelordstabilizer
name = "stabilizing serum"
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle19"
desc = "Inject certain types of monster organs with this stabilizer to preserve their healing powers indefinitely."
w_class = WEIGHT_CLASS_TINY
/obj/item/hivelordstabilizer/afterattack(obj/item/organ/M, mob/user)
. = ..()
var/obj/item/organ/regenerative_core/C = M
if(!istype(C, /obj/item/organ/regenerative_core))
to_chat(user, "<span class='warning'>The stabilizer only works on certain types of monster organs, generally regenerative in nature.</span>")
return ..()
C.preserved()
to_chat(user, "<span class='notice'>You inject the [M] with the stabilizer. It will no longer go inert.</span>")
qdel(src)
/************************Hivelord core*******************/
/obj/item/organ/regenerative_core
name = "regenerative core"
desc = "All that remains of a hivelord. It can be used to heal completely, but it will rapidly decay into uselessness."
icon_state = "roro core 2"
item_flags = NOBLUDGEON
slot = "hivecore"
force = 0
actions_types = list(/datum/action/item_action/organ_action/use)
var/inert = 0
var/preserved = 0
/obj/item/organ/regenerative_core/Initialize()
. = ..()
addtimer(CALLBACK(src, .proc/inert_check), 2400)
/obj/item/organ/regenerative_core/proc/inert_check()
if(!preserved)
go_inert()
/obj/item/organ/regenerative_core/proc/preserved(implanted = 0)
inert = FALSE
preserved = TRUE
update_icon()
desc = "All that remains of a hivelord. It is preserved, allowing you to use it to heal completely without danger of decay."
if(implanted)
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "implanted"))
else
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "stabilizer"))
/obj/item/organ/regenerative_core/proc/go_inert()
inert = TRUE
name = "decayed regenerative core"
desc = "All that remains of a hivelord. It has decayed, and is completely useless."
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "inert"))
update_icon()
/obj/item/organ/regenerative_core/ui_action_click()
if(inert)
to_chat(owner, "<span class='notice'>[src] breaks down as it tries to activate.</span>")
else
owner.revive(full_heal = 1)
owner.log_message("[owner] used an implanted [src] to heal themselves! Keep fighting, it's just a flesh wound!", LOG_ATTACK, color="green") //Logging for implanted legion core use
qdel(src)
/obj/item/organ/regenerative_core/on_life()
..()
if(owner.health < owner.crit_threshold)
ui_action_click()
/obj/item/organ/regenerative_core/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(proximity_flag && ishuman(target))
var/mob/living/carbon/human/H = target
if(inert)
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
return
else
if(H.stat == DEAD)
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
return
if(H != user)
H.visible_message("[user] forces [H] to apply [src]... [H.p_they()] quickly regenerate all injuries!")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "other"))
else
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
H.revive(full_heal = 1)
qdel(src)
user.log_message("[user] used [src] to heal [H]! Wake the fuck up, Samurai!", LOG_ATTACK, color="green") //Logging for 'old' style legion core use, when clicking on a sprite of yourself or another.
/obj/item/organ/regenerative_core/attack_self(mob/user) //Knouli's first hack! Allows for the use of the core in hand rather than needing to click on the target, yourself, to selfheal. Its a rip of the proc just above - but skips on distance check and only uses 'user' rather than 'target'
if(ishuman(user)) //Check if user is human, no need for distance check as it's self heal
var/mob/living/carbon/human/H = user //Set H to user rather than target
if(inert) //Inert cores are useless
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
return
else //Skip on check if the target to be healed is dead as, if you are dead, you're not going to be able to use it on yourself!
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
H.revive(full_heal = 1)
qdel(src)
H.log_message("[H] used [src] to heal themselves! Making use of Knouli's sexy and intelligent use-in-hand proc!", LOG_ATTACK, color="green") //Logging for 'new' style legion core use, when using the core in-hand.
/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
. = ..()
if(!preserved && !inert)
preserved(TRUE)
owner.visible_message("<span class='notice'>[src] stabilizes as it's inserted.</span>")
/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0)
if(!inert && !special)
owner.visible_message("<span class='notice'>[src] rapidly decays as it's removed.</span>")
go_inert()
return ..()
/obj/item/organ/regenerative_core/prepare_eat()
return null
/*************************Legion core********************/
/obj/item/organ/regenerative_core/legion
desc = "A strange rock that crackles with power. It can be used to heal completely, but it will rapidly decay into uselessness."
icon_state = "legion_soul"
/obj/item/organ/regenerative_core/legion/Initialize()
. = ..()
update_icon()
/obj/item/organ/regenerative_core/update_icon()
icon_state = inert ? "legion_soul_inert" : "legion_soul"
cut_overlays()
if(!inert && !preserved)
add_overlay("legion_soul_crackle")
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/obj/item/organ/regenerative_core/legion/go_inert()
..()
desc = "[src] has become inert. It has decayed, and is completely useless."
/obj/item/organ/regenerative_core/legion/preserved(implanted = 0)
..()
desc = "[src] has been stabilized. It is preserved, allowing you to use it to heal completely without danger of decay."
/*********************Hivelord stabilizer****************/
/obj/item/hivelordstabilizer
name = "stabilizing serum"
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle19"
desc = "Inject certain types of monster organs with this stabilizer to preserve their healing powers indefinitely."
w_class = WEIGHT_CLASS_TINY
/obj/item/hivelordstabilizer/afterattack(obj/item/organ/M, mob/user)
. = ..()
var/obj/item/organ/regenerative_core/C = M
if(!istype(C, /obj/item/organ/regenerative_core))
to_chat(user, "<span class='warning'>The stabilizer only works on certain types of monster organs, generally regenerative in nature.</span>")
return ..()
C.preserved()
to_chat(user, "<span class='notice'>You inject the [M] with the stabilizer. It will no longer go inert.</span>")
qdel(src)
/************************Hivelord core*******************/
/obj/item/organ/regenerative_core
name = "regenerative core"
desc = "All that remains of a hivelord. It can be used to heal completely, but it will rapidly decay into uselessness."
icon_state = "roro core 2"
item_flags = NOBLUDGEON
slot = "hivecore"
force = 0
actions_types = list(/datum/action/item_action/organ_action/use)
var/inert = 0
var/preserved = 0
/obj/item/organ/regenerative_core/Initialize()
. = ..()
addtimer(CALLBACK(src, .proc/inert_check), 2400)
/obj/item/organ/regenerative_core/proc/inert_check()
if(!preserved)
go_inert()
/obj/item/organ/regenerative_core/proc/preserved(implanted = 0)
inert = FALSE
preserved = TRUE
update_icon()
desc = "All that remains of a hivelord. It is preserved, allowing you to use it to heal completely without danger of decay."
if(implanted)
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "implanted"))
else
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "stabilizer"))
/obj/item/organ/regenerative_core/proc/go_inert()
inert = TRUE
name = "decayed regenerative core"
desc = "All that remains of a hivelord. It has decayed, and is completely useless."
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "inert"))
update_icon()
/obj/item/organ/regenerative_core/ui_action_click()
if(inert)
to_chat(owner, "<span class='notice'>[src] breaks down as it tries to activate.</span>")
else
owner.revive(full_heal = 1)
owner.log_message("[owner] used an implanted [src] to heal themselves! Keep fighting, it's just a flesh wound!", LOG_ATTACK, color="green") //Logging for implanted legion core use
qdel(src)
/obj/item/organ/regenerative_core/on_life()
..()
if(owner.health < owner.crit_threshold)
ui_action_click()
/obj/item/organ/regenerative_core/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(proximity_flag && ishuman(target))
var/mob/living/carbon/human/H = target
if(inert)
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
return
if(isvamp(user))
to_chat(user, "<span class='notice'>[src] breaks down as it fails to heal your unholy self</span>")
return
else
if(H.stat == DEAD)
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
return
if(H != user)
H.visible_message("[user] forces [H] to apply [src]... [H.p_they()] quickly regenerate all injuries!")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "other"))
else
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
H.revive(full_heal = 1)
qdel(src)
user.log_message("[user] used [src] to heal [H]! Wake the fuck up, Samurai!", LOG_ATTACK, color="green") //Logging for 'old' style legion core use, when clicking on a sprite of yourself or another.
/obj/item/organ/regenerative_core/attack_self(mob/user) //Knouli's first hack! Allows for the use of the core in hand rather than needing to click on the target, yourself, to selfheal. Its a rip of the proc just above - but skips on distance check and only uses 'user' rather than 'target'
if(ishuman(user)) //Check if user is human, no need for distance check as it's self heal
var/mob/living/carbon/human/H = user //Set H to user rather than target
if(inert) //Inert cores are useless
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
return
else //Skip on check if the target to be healed is dead as, if you are dead, you're not going to be able to use it on yourself!
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
H.revive(full_heal = 1)
qdel(src)
H.log_message("[H] used [src] to heal themselves! Making use of Knouli's sexy and intelligent use-in-hand proc!", LOG_ATTACK, color="green") //Logging for 'new' style legion core use, when using the core in-hand.
/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
. = ..()
if(!preserved && !inert)
preserved(TRUE)
owner.visible_message("<span class='notice'>[src] stabilizes as it's inserted.</span>")
/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0)
if(!inert && !special)
owner.visible_message("<span class='notice'>[src] rapidly decays as it's removed.</span>")
go_inert()
return ..()
/obj/item/organ/regenerative_core/prepare_eat()
return null
/*************************Legion core********************/
/obj/item/organ/regenerative_core/legion
desc = "A strange rock that crackles with power. It can be used to heal completely, but it will rapidly decay into uselessness."
icon_state = "legion_soul"
/obj/item/organ/regenerative_core/legion/Initialize()
. = ..()
update_icon()
/obj/item/organ/regenerative_core/update_icon()
icon_state = inert ? "legion_soul_inert" : "legion_soul"
cut_overlays()
if(!inert && !preserved)
add_overlay("legion_soul_crackle")
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/obj/item/organ/regenerative_core/legion/go_inert()
..()
desc = "[src] has become inert. It has decayed, and is completely useless."
/obj/item/organ/regenerative_core/legion/preserved(implanted = 0)
..()
desc = "[src] has been stabilized. It is preserved, allowing you to use it to heal completely without danger of decay."
+49
View File
@@ -0,0 +1,49 @@
/obj/machinery/point_bank
name = "mining point bank"
desc = "A wall mounted machine that can be used to store and transfer mining points. Sharing is caring!"
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "ore_redemption"
density = FALSE
req_access = list(ACCESS_MINERAL_STOREROOM)
circuit = null
layer = BELOW_OBJ_LAYER
var/points = 0
/obj/machinery/point_bank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "point_bank", "Point Bank", 200, 100, master_ui, state)
ui.open()
/obj/machinery/point_bank/ui_data(mob/user)
var/list/data = list()
data["totalPoints"] = points
return data
/obj/machinery/mineral/ore_redemption/ui_act(action, params)
if(..())
return
switch(action)
if("Claim")
var/mob/M = usr
var/obj/item/card/id/I = M.get_idcard(TRUE)
if(points)
if(I)
I.mining_points += points
points = 0
else
to_chat(usr, "<span class='warning'>No ID detected.</span>")
else
to_chat(usr, "<span class='warning'>No points to claim.</span>")
return TRUE
/obj/machinery/point_bank/power_change()
..()
update_icon()
/obj/machinery/point_bank/update_icon()
if(powered())
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]-off"
return
+1 -1
View File
@@ -988,4 +988,4 @@
if(H.clothing_flags & SCAN_REAGENTS)
return TRUE
if(isclothing(wear_mask) && (wear_mask.clothing_flags & SCAN_REAGENTS))
return TRUE
return TRUE
@@ -114,6 +114,10 @@
var/cansprint = 1
var/orebox = null
/mob/living/silicon/robot
/mob/living/silicon/robot/get_cell()
return cell
@@ -121,6 +121,10 @@
else if(istype(S, /obj/item/stack/marker_beacon))
S.cost = 1
S.source = get_or_create_estorage(/datum/robot_energy_storage/beacon)
else if(istype(S, /obj/item/stack/packageWrap))
S.cost = 1
S.source = get_or_create_estorage(/datum/robot_energy_storage/wrapping_paper)
if(S && S.source)
S.materials = list()
@@ -139,6 +143,9 @@
//Adds flavoursome dogborg items to dogborg variants without mechanical benefits
/obj/item/robot_module/proc/dogborg_equip()
has_snowflake_deadsprite = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
var/obj/item/I = new /obj/item/analyzer/nose/flavour(src)
basic_modules += I
I = new /obj/item/soap/tongue/flavour(src)
@@ -362,30 +369,21 @@
sleeper_overlay = "msleeper"
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
if("Medihound Dark")
cyborg_base_icon = "medihounddark"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "mdsleeper"
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
if("Vale")
cyborg_base_icon = "valemed"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "valemedsleeper"
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
if("Alina")
cyborg_base_icon = "alina-med"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
@@ -393,10 +391,7 @@
sleeper_overlay = "alinasleeper"
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
return ..()
/obj/item/robot_module/engineering
@@ -477,29 +472,20 @@
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
if("Pup Dozer")
cyborg_base_icon = "pupdozer"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "dozersleeper"
dogborg = TRUE
if("Vale")
cyborg_base_icon = "valeeng"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "valeengsleeper"
dogborg = TRUE
if("Alina")
cyborg_base_icon = "alina-eng"
special_light_key = "alina"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "alinasleeper"
dogborg = TRUE
return ..()
/obj/item/robot_module/security
@@ -558,36 +544,24 @@
if("K9")
cyborg_base_icon = "k9"
sleeper_overlay = "ksleeper"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
if("Alina")
cyborg_base_icon = "alina-sec"
special_light_key = "alina"
sleeper_overlay = "alinasleeper"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
if("K9 Dark")
cyborg_base_icon = "k9dark"
sleeper_overlay = "k9darksleeper"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
if("Vale")
cyborg_base_icon = "valesec"
sleeper_overlay = "valesecsleeper"
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
return ..()
/obj/item/robot_module/security/Initialize()
@@ -795,24 +769,18 @@
if("(Service) DarkK9")
cyborg_base_icon = "k50"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "ksleeper"
dogborg = TRUE
if("(Service) Vale")
cyborg_base_icon = "valeserv"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "valeservsleeper"
dogborg = TRUE
if("(Service) ValeDark")
cyborg_base_icon = "valeservdark"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
dogborg = TRUE
cyborg_pixel_offset = -16
sleeper_overlay = "valeservsleeper"
dogborg = TRUE
if("(Janitor) Default")
cyborg_base_icon = "janitor"
if("(Janitor) Marina")
@@ -830,10 +798,8 @@
if("(Janitor) Scrubpuppy")
cyborg_base_icon = "scrubpup"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
has_snowflake_deadsprite = TRUE
cyborg_pixel_offset = -16
dogborg = TRUE
sleeper_overlay = "jsleeper"
dogborg = TRUE
return ..()
/obj/item/robot_module/miner
@@ -845,13 +811,19 @@
/obj/item/borg/sight/meson,
/obj/item/storage/bag/ore/cyborg,
/obj/item/pickaxe/drill/cyborg,
/obj/item/shovel,
/obj/item/twohanded/kinetic_crusher/cyborg,
/obj/item/weldingtool/mini,
/obj/item/storage/bag/sheetsnatcher/borg,
/obj/item/t_scanner/adv_mining_scanner,
/obj/item/gun/energy/kinetic_accelerator/cyborg,
/obj/item/gun/energy/plasmacutter/cyborg,
/obj/item/gps/cyborg,
/obj/item/stack/marker_beacon)
/obj/item/weapon/gripper/mining,
/obj/item/cyborg_clamp,
/obj/item/card/id/miningborg,
/obj/item/stack/marker_beacon,
/obj/item/destTagger,
/obj/item/stack/packageWrap)
emag_modules = list(/obj/item/borg/stun)
ratvar_modules = list(
/obj/item/clockwork/slab/cyborg/miner,
@@ -863,7 +835,7 @@
/obj/item/robot_module/miner/be_transformed_to(obj/item/robot_module/old_module)
var/mob/living/silicon/robot/R = loc
var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Lavaland", "Heavy", "Sleek", "Marina", "Can", "Spider", "Asteroid", "Droid")
var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Lavaland", "Heavy", "Sleek", "Marina", "Can", "Spider", "Asteroid", "Droid", "Blade")
if(!borg_icon)
return FALSE
switch(borg_icon)
@@ -891,6 +863,11 @@
if("Heavy")
cyborg_base_icon = "heavymin"
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
if("Blade")
cyborg_base_icon = "blade"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "bladesleeper"
dogborg = TRUE
return ..()
/obj/item/robot_module/syndicate
@@ -1036,3 +1013,8 @@
max_energy = 30
recharge_rate = 1
name = "Marker Beacon Storage"
/datum/robot_energy_storage/wrapping_paper
max_energy = 30
recharge_rate = 1
name = "Wrapping Paper Storage"
@@ -151,7 +151,8 @@
return
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
if(..())
. = ..()
if(. && !QDELETED(src)) //Got hit and not blown up yet.
if(prob(50) && !isnull(load))
unload(0)
if(prob(25))
@@ -38,6 +38,10 @@
. = ..()
verbs += /mob/living/proc/lay_down
/mob/living/simple_animal/pet/cat/ComponentInitialize()
. = ..()
AddElement(/datum/element/wuv, "purrs!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "hisses!", EMOTE_AUDIBLE)
/mob/living/simple_animal/pet/cat/update_canmove()
..()
if(client && stat != DEAD)
@@ -229,24 +233,6 @@
stop_automated_movement = 1
walk_to(src,movement_target,0,3)
/mob/living/simple_animal/pet/cat/attack_hand(mob/living/carbon/human/M)
. = ..()
switch(M.a_intent)
if(INTENT_HELP)
wuv(1, M)
if(INTENT_HARM)
wuv(-1, M)
/mob/living/simple_animal/pet/cat/proc/wuv(change, mob/M)
if(change)
if(change > 0)
if(M && stat != DEAD)
new /obj/effect/temp_visual/heart(loc)
emote("me", EMOTE_VISIBLE, "purrs!")
else
if(M && stat != DEAD)
emote("me", EMOTE_VISIBLE, "hisses!")
/mob/living/simple_animal/pet/cat/cak //I told you I'd do it, Remie
name = "Keeki"
desc = "It's a cat made out of cake."
@@ -16,6 +16,11 @@
do_footstep = TRUE
can_be_held = TRUE
/mob/living/simple_animal/pet/dog/ComponentInitialize()
. = ..()
AddElement(/datum/element/wuv, "yaps_happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
//Corgis and pugs are now under one dog subtype
/mob/living/simple_animal/pet/dog/corgi
@@ -268,7 +273,7 @@
return
if(!item_to_add)
user.visible_message("[user] pets [src].","<span class='notice'>You rest your hand on [src]'s head for a moment.</span>")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "pet_corgi", /datum/mood_event/pet_corgi)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, src, /datum/mood_event/pet_animal, src)
return
if(user && !user.temporarilyRemoveItemFromInventory(item_to_add))
@@ -639,22 +644,3 @@
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
setDir(i)
sleep(1)
/mob/living/simple_animal/pet/dog/attack_hand(mob/living/carbon/human/M)
. = ..()
switch(M.a_intent)
if(INTENT_HELP)
wuv(1,M)
if(INTENT_HARM)
wuv(-1,M)
/mob/living/simple_animal/pet/dog/proc/wuv(change, mob/M)
if(change)
if(change > 0)
if(M && stat != DEAD) // Added check to see if this mob (the dog) is dead to fix issue 2454
new /obj/effect/temp_visual/heart(loc)
emote("me", EMOTE_VISIBLE, "yaps happily!")
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "pet_corgi", /datum/mood_event/pet_corgi)
else
if(M && stat != DEAD) // Same check here, even though emote checks it as well (poor form to check it only in the help case)
emote("me", EMOTE_VISIBLE, "growls!")
@@ -240,7 +240,8 @@ obj/structure/elite_tumor/proc/return_elite()
INVOKE_ASYNC(src, .proc/fighters_check) //Checks to see if our fighters died.
INVOKE_ASYNC(src, .proc/arena_trap) //Gets another arena trap queued up for when this one runs out.
INVOKE_ASYNC(src, .proc/border_check) //Checks to see if our fighters got out of the arena somehow.
addtimer(CALLBACK(src, .proc/arena_checks), 50)
if(!QDELETED(src))
addtimer(CALLBACK(src, .proc/arena_checks), 50)
/obj/structure/elite_tumor/proc/fighters_check()
if(activator != null && activator.stat == DEAD || activity == TUMOR_ACTIVE && QDELETED(activator))
+1
View File
@@ -29,6 +29,7 @@
var/obj/machinery/machine = null
var/next_move = null
var/create_area_cooldown
var/notransform = null //Carbon
var/eye_blind = 0 //Carbon
var/eye_blurry = 0 //Carbon
+2 -2
View File
@@ -45,8 +45,8 @@
eject()
else
var/gasdrained = min(powerproduction_drain*drainratio,loaded_tank.air_contents.gases[/datum/gas/plasma])
loaded_tank.air_contents.gases[/datum/gas/plasma] -= gasdrained
loaded_tank.air_contents.gases[/datum/gas/tritium] += gasdrained
loaded_tank.air_contents.gases[/datum/gas/plasma] -= 2.7 * gasdrained
loaded_tank.air_contents.gases[/datum/gas/tritium] += 2.7 * gasdrained
GAS_GARBAGE_COLLECT(loaded_tank.air_contents.gases)
var/power_produced = RAD_COLLECTOR_OUTPUT
@@ -9,3 +9,7 @@
projectile_type = /obj/item/projectile/plasma/adv
delay = 10
e_cost = 10
/obj/item/ammo_casing/energy/plasma/weak
projectile_type = /obj/item/projectile/plasma/weak
e_cost = 100
@@ -112,7 +112,10 @@
/obj/item/gun/energy/kinetic_accelerator/cyborg
holds_charge = TRUE
unique_frequency = TRUE
max_mod_capacity = 80
/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg
holds_charge = TRUE
unique_frequency = TRUE
/obj/item/gun/energy/kinetic_accelerator/minebot
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
@@ -280,7 +283,7 @@
. += "<span class='notice'>Occupies <b>[cost]%</b> of mod capacity.</span>"
/obj/item/borg/upgrade/modkit/attackby(obj/item/A, mob/user)
if(istype(A, /obj/item/gun/energy/kinetic_accelerator) && !issilicon(user))
if(istype(A, /obj/item/gun/energy/kinetic_accelerator))
install(A, user)
else
..()
@@ -47,3 +47,9 @@
damage = 24
range = 7
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
/obj/item/projectile/plasma/weak
dismemberment = 0
damage = 10
range = 4
mine_range = 0
@@ -155,7 +155,7 @@
reagent_state = SOLID
color = "#FFFFFF" // rgb: 255, 255, 255
taste_mult = 1.5 // stop sugar drowning out other flavours
nutriment_factor = 10 * REAGENTS_METABOLISM
nutriment_factor = 5 * REAGENTS_METABOLISM
metabolization_rate = 2 * REAGENTS_METABOLISM
overdose_threshold = 200 // Hyperglycaemic shock
taste_description = "sweetness"
@@ -624,12 +624,21 @@
construction_time = 120
category = list("Cyborg Upgrade Modules")
/datum/design/borg_upgrade_holding
name = "Cyborg Upgrade (Ore Satchel of Holding)"
id = "borg_upgrade_holding"
/datum/design/borg_upgrade_advcutter
name = "Cyborg Upgrade (Advanced Plasma Cutter)"
id = "borg_upgrade_advcutter"
build_type = MECHFAB
build_path = /obj/item/borg/upgrade/soh
materials = list(MAT_METAL = 10000, MAT_GOLD = 250, MAT_URANIUM = 500)
build_path = /obj/item/borg/upgrade/advcutter
materials = list(MAT_METAL=8000, MAT_PLASMA=2000, MAT_GOLD= 2000)
construction_time = 120
category = list("Cyborg Upgrade Modules")
/datum/design/borg_upgrade_premiumka
name = "Cyborg Upgrade (Premium Kinetic Accelerator)"
id = "borg_upgrade_premiumka"
build_type = MECHFAB
build_path = /obj/item/borg/upgrade/premiumka
materials = list(MAT_METAL=8000, MAT_GLASS=4000, MAT_TITANIUM=2000)
construction_time = 120
category = list("Cyborg Upgrade Modules")
+2 -2
View File
@@ -365,7 +365,7 @@
display_name = "Advanced Robotics Research"
description = "It can even do the dishes!"
prereq_ids = list("robotics")
design_ids = list("borg_upgrade_diamonddrill", "borg_upgrade_advancedmop")
design_ids = list("borg_upgrade_diamonddrill", "borg_upgrade_advancedmop", "borg_upgrade_advcutter", "borg_upgrade_premiumka")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
export_price = 5000
@@ -391,7 +391,7 @@
display_name = "Cyborg Upgrades: Utility"
description = "Utility upgrades for cyborgs."
prereq_ids = list("engineering", "robotics")
design_ids = list("borg_upgrade_holding", "borg_upgrade_lavaproof", "borg_upgrade_thrusters", "borg_upgrade_selfrepair", "borg_upgrade_expand", "borg_upgrade_rped")
design_ids = list("borg_upgrade_lavaproof", "borg_upgrade_thrusters", "borg_upgrade_selfrepair", "borg_upgrade_expand", "borg_upgrade_rped")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
export_price = 5000
+2 -3
View File
@@ -172,9 +172,8 @@
var/total_damage = brute + burn
if(total_damage > can_inflict)
var/excess = total_damage - can_inflict
brute = round(brute * (excess / total_damage),DAMAGE_PRECISION)
burn = round(burn * (excess / total_damage),DAMAGE_PRECISION)
brute = round(brute * (max_damage / total_damage),DAMAGE_PRECISION)
burn = round(burn * (max_damage / total_damage),DAMAGE_PRECISION)
brute_dam += brute
burn_dam += burn
@@ -22,11 +22,14 @@
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered)
drop_limb()
C.bleed(40)
if(QDELETED(src)) //Could have dropped into lava/explosion/chasm/whatever
return TRUE
if(dam_type == BURN)
burn()
return 1
return TRUE
add_mob_blood(C)
C.bleed(40)
var/direction = pick(GLOB.cardinals)
var/t_range = rand(2,max(throw_range/2, 2))
var/turf/target_turf = get_turf(src)
@@ -38,7 +41,7 @@
if(new_turf.density)
break
throw_at(target_turf, throw_range, throw_speed)
return 1
return TRUE
/obj/item/bodypart/chest/dismember()
@@ -149,7 +152,6 @@
LB.brainmob = brainmob
brainmob = null
LB.brainmob.forceMove(LB)
LB.brainmob.container = LB
LB.brainmob.stat = DEAD
/obj/item/organ/eyes/transfer_to_limb(obj/item/bodypart/head/LB, mob/living/carbon/human/C)
+16 -16
View File
@@ -19,7 +19,7 @@
TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!")
warned_command_names[command_name] = TRUE
continue
if(command_name_types[command_name])
if(warnings_only)
TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
@@ -55,24 +55,24 @@ The MIT License
Copyright (c) 2017 Jordan Brown
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+1
View File
@@ -1,6 +1,7 @@
/obj/machinery/vending/assist
products = list(/obj/item/assembly/prox_sensor = 7,
/obj/item/assembly/igniter = 6,
/obj/item/assembly/playback = 4,
/obj/item/assembly/signaler = 6,
/obj/item/wirecutters = 3,
/obj/item/stock_parts/cell/crap = 6,
+12
View File
@@ -473,3 +473,15 @@ DISABLE_HIGH_POP_MC_MODE_AMOUNT 60
## For reference, Goonstation uses a resolution of 21x15 for it's widescreen mode.
## Do note that changing this value will affect the title screen. The title screen will have to be updated manually if this is changed.
DEFAULT_VIEW 21x15
### FAIL2TOPIC:
### Automated IP bans for world/Topic() spammers
## Enabled
FAIL2TOPIC_ENABLED
## Minimum wait time in deciseconds between valid requests
FAIL2TOPIC_RATE_LIMIT 10
## Number of requests after breaching rate limit that triggers a ban
FAIL2TOPIC_MAX_FAILS 5
## Firewall rule name used on physical server
FAIL2TOPIC_RULE_NAME _dd_fail2topic
+6
View File
@@ -582,3 +582,9 @@ BOX_RANDOM_ENGINE Box Singulo,3
BOX_RANDOM_ENGINE Box SM 1x3,1
BOX_RANDOM_ENGINE Box SM 5x5,1
BOX_RANDOM_ENGINE Box SM 3x,0
## Whether or not there's a mode tier list vote after the secret/extended vote.
MODETIER_VOTING
## Number of modes dropped by the modetier vote during mode selection, after vote.
DROPPED_MODES 3
@@ -0,0 +1,4 @@
author: "Ghommie"
delete-after: True
changes:
- bugfix: "Fixed singularity pulls duping rods out of engine floors."
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Increases plasma usage in radiation collectors"
@@ -0,0 +1,4 @@
author: "LetterN"
delete-after: True
changes:
- rscadd: "Doppler logs"
@@ -0,0 +1,5 @@
author: "Putnam3145"
delete-after: True
changes:
- rscadd: "Added a sort of \"game mode ban\" by way of having people rank their game modes favorite to least favorite after the secret/extended vote."
- bugfix: "Turns out the schulze scoring was written wrong and it was setting things to 0 that shouldn't have been, so that's fixed."
@@ -0,0 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- tweak: "Halfs the nutriments in sugar"
@@ -0,0 +1,4 @@
author: "Ghommie"
delete-after: True
changes:
- bugfix: "Lattices can be examined yet again."
@@ -0,0 +1,5 @@
author: "r4d6"
delete-after: True
changes:
- rscadd: "Added a playback device"
- bugfix: "Made the Voice Analyzer actually care about languages"
@@ -0,0 +1,4 @@
author: "Putnam"
delete-after: True
changes:
- rscadd: "Cold-blooded quirk"
@@ -0,0 +1,4 @@
author: "Seris02"
delete-after: True
changes:
- bugfix: "cardboard box speed"
@@ -0,0 +1,4 @@
author: "keronshb"
delete-after: True
changes:
- bugfix: "fixed the missing icons from Dermal Button nanites"
@@ -0,0 +1,4 @@
author: "Bhijn"
delete-after: True
changes:
- bugfix: "server_hop can no longer be used to remotely lobotomize a spaceman"
@@ -0,0 +1,5 @@
author: "Putnam3145"
delete-after: True
changes:
- bugfix: "Traitor objectives don't take a dump when they try to add an assassinate anymore"
- admin: "Objective removal with antag panel no longer commented out silently while still being an option that gives useful feedback on stuff it's not doing in any respect"
@@ -0,0 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- bugfix: "Limb damage works now"
@@ -0,0 +1,4 @@
author: "Ghommie"
delete-after: True
changes:
- tweak: "add a click cooldown to the overly spammable table slamming."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

@@ -1,2 +0,0 @@
/obj/machinery/sleeper
min_health = 30

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