diff --git a/_maps/templates/shelter_1.dmm b/_maps/templates/shelter_1.dmm index f5b2e141f4..7f95fba10f 100644 --- a/_maps/templates/shelter_1.dmm +++ b/_maps/templates/shelter_1.dmm @@ -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" = ( diff --git a/_maps/templates/shelter_2.dmm b/_maps/templates/shelter_2.dmm index 80de4438da..825cb26e3a 100644 --- a/_maps/templates/shelter_2.dmm +++ b/_maps/templates/shelter_2.dmm @@ -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" = ( diff --git a/_maps/templates/shelter_3.dmm b/_maps/templates/shelter_3.dmm index b71da1fba0..bb400f29ac 100644 --- a/_maps/templates/shelter_3.dmm +++ b/_maps/templates/shelter_3.dmm @@ -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 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c194e578c9..5c54843df2 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -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 diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 3ee77d3edc..4dd590d9a1 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -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 diff --git a/code/__HELPERS/areas.dm b/code/__HELPERS/areas.dm index 1f5b82f7bf..4b52187e13 100644 --- a/code/__HELPERS/areas.dm +++ b/code/__HELPERS/areas.dm @@ -43,6 +43,13 @@ var/static/blacklisted_areas = typecacheof(list( /area/space, )) + + if(creator) + if(creator.create_area_cooldown >= world.time) + to_chat(creator, "You're trying to create a new area a little too fast.") + 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, "The new area must be completely airtight and not a part of a shuttle.") diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 5ca3513e66..01d5051dba 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -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) diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 14954524fa..730a3f17f4 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -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]) diff --git a/code/controllers/configuration/entries/fail2topic.dm b/code/controllers/configuration/entries/fail2topic.dm new file mode 100644 index 0000000000..7ed09b378a --- /dev/null +++ b/code/controllers/configuration/entries/fail2topic.dm @@ -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 diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 5a0eb0c65f..8b6bbe83b4 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -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 diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 125da84a30..7244212630 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -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) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 4fe0812c56..3be4f36270 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -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, "[msg]") - 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. diff --git a/code/controllers/subsystem/fail2topic.dm b/code/controllers/subsystem/fail2topic.dm new file mode 100644 index 0000000000..a589ae2462 --- /dev/null +++ b/code/controllers/subsystem/fail2topic.dm @@ -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.") diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 45c8e3c8fe..0948e428ff 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -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 diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 573c89a5af..f82954276e 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -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[choices[i]]: [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,"The vote will end right as the round starts.") + 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 diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm new file mode 100644 index 0000000000..84f327500f --- /dev/null +++ b/code/datums/elements/wuv.dm @@ -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) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 5c92c83fb5..8ae45ff720 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -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]") diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index 94fd08535f..98a8eade59 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -23,10 +23,13 @@ mood_change = 3 timeout = 3000 -/datum/mood_event/pet_corgi - description = "Corgis are adorable! I can't stop petting them!\n" - mood_change = 3 - timeout = 3000 +/datum/mood_event/pet_animal + description = "Animals are adorable! I can't stop petting them!\n" + mood_change = 2 + timeout = 5 MINUTES + +/datum/mood_event/pet_animal/add_effects(mob/animal) + description = "\The [animal.name] is adorable! I can't stop petting [animal.p_them()]!\n" /datum/mood_event/honk description = "Maybe clowns aren't so bad after all. Honk!\n" diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index f5f012e7f9..c3f74ad44e 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -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.." diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm index e712a38df1..8be3174f50 100644 --- a/code/datums/traits/neutral.dm +++ b/code/datums/traits/neutral.dm @@ -122,6 +122,15 @@ gain_text = "You feel like exposing yourself to the world." lose_text = "Indecent exposure doesn't sound as charming to you anymore." +/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 = "You feel cold-blooded." + lose_text = "You feel more warm-blooded." + /datum/quirk/alcohol_intolerance name = "Alcohol Intolerance" desc = "You take toxin damage from alcohol rather than getting drunk." diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm index 0c43d33a4b..e01b525e02 100644 --- a/code/datums/world_topic.dm +++ b/code/datums/world_topic.dm @@ -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("HELP: [input["source"]] [input["message_sender"]]: [input["message"]]") /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 diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm index e784fd836d..ca2ebfe00d 100644 --- a/code/game/gamemodes/bloodsucker/bloodsucker.dm +++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm @@ -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" diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 520721560a..b0c7299b34 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -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( diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index e683d67be2..97b260f3bf 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -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() diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 1c25c7b242..65f7602215 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -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) . = ..() - . += "Its dish is facing to the [dir2text(dir)]." + 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]]

" + dat += "Delete logs
" + dat += "
" + dat += "(Refresh)
" + dat += "" + 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, "You adjust [src]'s dish to face to the [dir2text(dir)].") 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) + . = ..() + . += "Its dish is facing to the [dir2text(dir)]." + +/obj/machinery/doppler_array/process() + return PROCESS_KILL + /obj/machinery/doppler_array/power_change() if(stat & BROKEN) icon_state = "[initial(icon_state)]-broken" diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 71f39608b5..8f7fce8e3e 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -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 diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 4ed541ef10..c0f563d44d 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -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 diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index d97c07bf87..ccffa413a6 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -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, "This PDA does not have an ID in it!") @@ -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() diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index c09f5b1b65..a0d58709c8 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -814,3 +814,93 @@ return else to_chat(user, "Your gripper cannot hold \the [target].") + +/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, "Error: Living entity detected in [target]. Cannot load.") + O.anchored = initial(O.anchored) + return + for(var/obj/item/disk/nuclear/diskie in target.GetAllContents()) + to_chat(user, "Error: Nuclear class authorization device detected in [target]. Cannot load.") + 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, "[target] successfully loaded.") + playsound(loc, 'sound/effects/bin_close.ogg', 50, 0) + else + to_chat(user, "Not enough room in cargo compartment! Maximum of [cargo_capacity] objects!") + O.anchored = initial(O.anchored) + return + else + O.anchored = initial(O.anchored) + else + to_chat(user, "Not enough room in cargo compartment! Maximum of eight objects!") + else + to_chat(user, "[target] is firmly secured!") + +/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" \ No newline at end of file diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index bf363f959c..cc1cfec818 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -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" diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index eafa79798e..7664da6ce4 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -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() . = ..() diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index e61a17b4ec..9fa4d730b6 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -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, "You can't do that right now!") + return TRUE + if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) + to_chat(user, "You're too exhausted for that.") + return TRUE + var/mob/living/poordude = buckled_mobs[1] + if(!istype(poordude)) + return TRUE + user.visible_message("[user] pulls [src] out from under [poordude].", "You pull [src] out from under [poordude].") + 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)) ..() diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index aad68b2166..645d1e5d7a 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -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() diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 771b4bc04e..8b078802f0 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -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." diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index c0519f2504..205e87329b 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -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) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index a7eaea0d16..c41d6d32af 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -18,7 +18,7 @@ // flags = CONDUCT_1 /obj/structure/lattice/examine(mob/user) - ..() + . = ..() . += deconstruction_hints(user) /obj/structure/lattice/proc/deconstruction_hints(mob/user) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 99f8875aef..0bb081625b 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -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("[user] slams [user.p_their()] palms down on [src].", "You slam your palms down on [src].") + playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1) + else + user.visible_message("[user] slaps [user.p_their()] hands on [src].", "You slap your hands on [src].") + 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)) diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 3f46994da9..f9153ff8bf 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -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) . = ..() diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index 82f1a88253..f38a8a3d9b 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -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 diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm index 28ffbbd1ff..7816341a0a 100644 --- a/code/game/turfs/simulated/floor/reinf_floor.dm +++ b/code/game/turfs/simulated/floor/reinf_floor.dm @@ -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) diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index aeadceb2e5..73c1c465cd 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -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, "You don't have the dexterity to do this!") return @@ -63,7 +64,12 @@ to_chat(user, "You start picking...") 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, "You finish cutting into the rock.") gets_drilled(user) SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type) diff --git a/code/game/world.dm b/code/game/world.dm index 4043f15f6f..034ecddfa2 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -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 diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm index 9174692b49..bbb8aeca30 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm @@ -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, "Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.") + sleep(30) //To avoid spam if (poweron_masquerade == TRUE) to_chat(owner, "Your wounds will not heal until you disable the Masquerade power.") // 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("[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.", \ "Your soul escapes your withering body as the abyss welcomes you to your Final Death.", \ "You hear a dry, crackling sound.") @@ -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 diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm index 97b4437298..844b523135 100644 --- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm +++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm @@ -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, "You are now a rank [vamplevel] Bloodsucker. Your strength, resistence, health, feed rate, regen rate, and maximum blood have all increased!") + to_chat(owner.current, "You are now a rank [vamplevel] Bloodsucker. Your strength, health, feed rate, regen rate, and maximum blood have all increased!") to_chat(owner.current, "Your existing powers have all ranked up as well!") 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. diff --git a/code/modules/antagonists/bloodsucker/datum_vassal.dm b/code/modules/antagonists/bloodsucker/datum_vassal.dm index 71ee0bcc1d..716b7ff223 100644 --- a/code/modules/antagonists/bloodsucker/datum_vassal.dm +++ b/code/modules/antagonists/bloodsucker/datum_vassal.dm @@ -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) diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm index 5a905857c5..e4e26fe034 100644 --- a/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm +++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm @@ -51,6 +51,11 @@ return "no" // 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 diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm index 9db4cae1ff..b7c90523b6 100644 --- a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm +++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.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, "You cant stake [target] when they are moving moving about! They have to be laying down!") + to_chat(user, "You can't stake [target] when they are moving about! They have to be laying down or grabbed by the neck!") 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 diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm index 3493622945..f66ce4a208 100644 --- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm +++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm @@ -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, "Until this rack is secured in place, it cannot serve its purpose.") 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("[user] tries to release themself from the rack!",\ "You attempt to release yourself from the rack!") // For sound if not seen --> "You hear a squishy wet noise.") @@ -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)) . += {"This is a magical candle which drains at the sanity of mortals who are not under your command while it is active."} . += {"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."} /* 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"} */ /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) diff --git a/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm b/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm index cea942e26d..9126638fea 100644 --- a/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm +++ b/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm @@ -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() diff --git a/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm b/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm index 6ee17b3014..0435ddccd5 100644 --- a/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm +++ b/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm @@ -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, "Your heart beats one final time, while your skin dries out and your icy pallor returns.") diff --git a/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm index 94bc0e11d0..7962c7d403 100644 --- a/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm +++ b/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm @@ -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, "[target] is fixed in place by your hypnotic gaze.") 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) diff --git a/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm b/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm index c650a6af15..159c7b20b7 100644 --- a/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm +++ b/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm @@ -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) diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index d7d88a9b6a..b98fa04504 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -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 diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 40d1712bc3..595ce5bb90 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -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() diff --git a/code/modules/assembly/playback.dm b/code/modules/assembly/playback.dm new file mode 100644 index 0000000000..088f186adc --- /dev/null +++ b/code/modules/assembly/playback.dm @@ -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 \ No newline at end of file diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index f72f726988..36a58b2a70 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -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) diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index aa1abe56b4..d885372f5f 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -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" diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 29c4c3d833..f84bd0d8d6 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -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 diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index eb3e6a5b58..b2c0c1cc87 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -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 diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index e02f38b7e3..d776628def 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -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, "You increase the tools dig range, decreasing its mining speed.") + else + digrange = 0 + toolspeed = toolspeed/2 + to_chat(user, "You decrease the tools dig range, increasing its mining speed.") + else + to_chat(user, "Tool does not have a configureable dig range.") /obj/item/pickaxe/suicide_act(mob/living/user) user.visible_message("[user] begins digging into [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!") @@ -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" diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index 4429c8426f..1e4244165f 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -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, "The stabilizer only works on certain types of monster organs, generally regenerative in nature.") - return ..() - - C.preserved() - to_chat(user, "You inject the [M] with the stabilizer. It will no longer go inert.") - 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, "[src] breaks down as it tries to activate.") - 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, "[src] has decayed and can no longer be used to heal.") - return - else - if(H.stat == DEAD) - to_chat(user, "[src] are useless on the dead.") - 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, "You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.") - 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, "[src] has decayed and can no longer be used to heal.") - 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, "You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.") - 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("[src] stabilizes as it's inserted.") - -/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0) - if(!inert && !special) - owner.visible_message("[src] rapidly decays as it's removed.") - 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, "The stabilizer only works on certain types of monster organs, generally regenerative in nature.") + return ..() + + C.preserved() + to_chat(user, "You inject the [M] with the stabilizer. It will no longer go inert.") + 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, "[src] breaks down as it tries to activate.") + 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, "[src] has decayed and can no longer be used to heal.") + return + if(isvamp(user)) + to_chat(user, "[src] breaks down as it fails to heal your unholy self") + return + else + if(H.stat == DEAD) + to_chat(user, "[src] are useless on the dead.") + 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, "You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.") + 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, "[src] has decayed and can no longer be used to heal.") + 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, "You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.") + 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("[src] stabilizes as it's inserted.") + +/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0) + if(!inert && !special) + owner.visible_message("[src] rapidly decays as it's removed.") + 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." diff --git a/code/modules/mining/point_bank.dm b/code/modules/mining/point_bank.dm new file mode 100644 index 0000000000..8d0bb4e1e4 --- /dev/null +++ b/code/modules/mining/point_bank.dm @@ -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, "No ID detected.") + else + to_chat(usr, "No points to claim.") + 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 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 102bc6fc3d..998fd3770f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -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 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 5f7d3ca243..86e3ccad24 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -114,6 +114,10 @@ var/cansprint = 1 + var/orebox = null + +/mob/living/silicon/robot + /mob/living/silicon/robot/get_cell() return cell diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index ece7fe9ecb..1d8e5ed21b 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -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" diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 426de81da0..43963b4eef 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -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)) diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index bda309f7c7..00f7b8cab7 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -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." diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 986e5c9b4d..2371cfd7f1 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -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].","You rest your hand on [src]'s head for a moment.") - 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!") diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 0a8320788e..199def430e 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -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)) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index c7365210ab..695084a794 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -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 diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 054b91f273..91a2bc515f 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -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 diff --git a/code/modules/projectiles/ammunition/energy/plasma.dm b/code/modules/projectiles/ammunition/energy/plasma.dm index d02abf9c88..3a71254508 100644 --- a/code/modules/projectiles/ammunition/energy/plasma.dm +++ b/code/modules/projectiles/ammunition/energy/plasma.dm @@ -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 \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index b216d8d536..ecd906f2a9 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -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 @@ . += "Occupies [cost]% of mod capacity." /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 ..() diff --git a/code/modules/projectiles/projectile/special/plasma.dm b/code/modules/projectiles/projectile/special/plasma.dm index aeafb6157a..4c05c42fb3 100644 --- a/code/modules/projectiles/projectile/special/plasma.dm +++ b/code/modules/projectiles/projectile/special/plasma.dm @@ -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 \ No newline at end of file diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 64e693a326..8936866305 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -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" diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 113a6262c9..5fab7705a8 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -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") diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index af4148d194..a44495fccb 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -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 diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index a1b74942e0..63a392bf06 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -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 diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 31f08f0e87..31fc90cb04 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -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) diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm index 71d7e32366..e674fd4e78 100644 --- a/code/modules/tgs/v3210/commands.dm +++ b/code/modules/tgs/v3210/commands.dm @@ -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. */ diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm index 280bcb5ca2..0d0dd40b36 100644 --- a/code/modules/vending/assist.dm +++ b/code/modules/vending/assist.dm @@ -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, diff --git a/config/config.txt b/config/config.txt index e71c2587b7..fafb3e5791 100644 --- a/config/config.txt +++ b/config/config.txt @@ -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 + diff --git a/config/game_options.txt b/config/game_options.txt index d68143a35c..060d782dbe 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -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 diff --git a/html/changelogs/AutoChangeLog-pr-10285.yml b/html/changelogs/AutoChangeLog-pr-10285.yml new file mode 100644 index 0000000000..89b0bbae43 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10285.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed singularity pulls duping rods out of engine floors." diff --git a/html/changelogs/AutoChangeLog-pr-10420.yml b/html/changelogs/AutoChangeLog-pr-10420.yml new file mode 100644 index 0000000000..b719eca84c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10420.yml @@ -0,0 +1,4 @@ +author: "MrJWhit" +delete-after: True +changes: + - tweak: "Increases plasma usage in radiation collectors" diff --git a/html/changelogs/AutoChangeLog-pr-10439.yml b/html/changelogs/AutoChangeLog-pr-10439.yml new file mode 100644 index 0000000000..92b151b883 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10439.yml @@ -0,0 +1,4 @@ +author: "LetterN" +delete-after: True +changes: + - rscadd: "Doppler logs" diff --git a/html/changelogs/AutoChangeLog-pr-10440.yml b/html/changelogs/AutoChangeLog-pr-10440.yml new file mode 100644 index 0000000000..e942f4056f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10440.yml @@ -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." diff --git a/html/changelogs/AutoChangeLog-pr-10442.yml b/html/changelogs/AutoChangeLog-pr-10442.yml new file mode 100644 index 0000000000..f407ae9e15 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10442.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "Halfs the nutriments in sugar" diff --git a/html/changelogs/AutoChangeLog-pr-10452.yml b/html/changelogs/AutoChangeLog-pr-10452.yml new file mode 100644 index 0000000000..e51b9ff752 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10452.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Lattices can be examined yet again." diff --git a/html/changelogs/AutoChangeLog-pr-10457.yml b/html/changelogs/AutoChangeLog-pr-10457.yml new file mode 100644 index 0000000000..203842b0b6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10457.yml @@ -0,0 +1,5 @@ +author: "r4d6" +delete-after: True +changes: + - rscadd: "Added a playback device" + - bugfix: "Made the Voice Analyzer actually care about languages" diff --git a/html/changelogs/AutoChangeLog-pr-10459.yml b/html/changelogs/AutoChangeLog-pr-10459.yml new file mode 100644 index 0000000000..77de8eb193 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10459.yml @@ -0,0 +1,4 @@ +author: "Putnam" +delete-after: True +changes: + - rscadd: "Cold-blooded quirk" diff --git a/html/changelogs/AutoChangeLog-pr-10467.yml b/html/changelogs/AutoChangeLog-pr-10467.yml new file mode 100644 index 0000000000..7fa379f088 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10467.yml @@ -0,0 +1,4 @@ +author: "Seris02" +delete-after: True +changes: + - bugfix: "cardboard box speed" diff --git a/html/changelogs/AutoChangeLog-pr-10472.yml b/html/changelogs/AutoChangeLog-pr-10472.yml new file mode 100644 index 0000000000..9404d70b7b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10472.yml @@ -0,0 +1,4 @@ +author: "keronshb" +delete-after: True +changes: + - bugfix: "fixed the missing icons from Dermal Button nanites" diff --git a/html/changelogs/AutoChangeLog-pr-10473.yml b/html/changelogs/AutoChangeLog-pr-10473.yml new file mode 100644 index 0000000000..f1821a3cce --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10473.yml @@ -0,0 +1,4 @@ +author: "Bhijn" +delete-after: True +changes: + - bugfix: "server_hop can no longer be used to remotely lobotomize a spaceman" diff --git a/html/changelogs/AutoChangeLog-pr-10474.yml b/html/changelogs/AutoChangeLog-pr-10474.yml new file mode 100644 index 0000000000..ae7b5b3fcb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10474.yml @@ -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" diff --git a/html/changelogs/AutoChangeLog-pr-10483.yml b/html/changelogs/AutoChangeLog-pr-10483.yml new file mode 100644 index 0000000000..1f016b1591 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10483.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - bugfix: "Limb damage works now" diff --git a/html/changelogs/AutoChangeLog-pr-9878.yml b/html/changelogs/AutoChangeLog-pr-9878.yml new file mode 100644 index 0000000000..ef9c704003 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9878.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - tweak: "add a click cooldown to the overly spammable table slamming." diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi index 39e2ea0584..43c9d20a93 100644 Binary files a/icons/mob/actions/actions_items.dmi and b/icons/mob/actions/actions_items.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 73eb12fd6a..4cb7e02e03 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/modular_citadel/code/game/machinery/Sleeper.dm b/modular_citadel/code/game/machinery/Sleeper.dm deleted file mode 100644 index 4cf04524b2..0000000000 --- a/modular_citadel/code/game/machinery/Sleeper.dm +++ /dev/null @@ -1,2 +0,0 @@ -/obj/machinery/sleeper - min_health = 30 diff --git a/modular_citadel/code/game/machinery/displaycases.dm b/modular_citadel/code/game/machinery/displaycases.dm deleted file mode 100644 index f660475cfc..0000000000 --- a/modular_citadel/code/game/machinery/displaycases.dm +++ /dev/null @@ -1,5 +0,0 @@ -/obj/structure/displaycase/clown - desc = "In the event of clown, honk glass." - alert = 1 - start_showpiece_type = /obj/item/bikehorn - req_access = list(ACCESS_CENT_GENERAL) diff --git a/modular_citadel/code/game/objects/structures/beds_chairs/chair.dm b/modular_citadel/code/game/objects/structures/beds_chairs/chair.dm deleted file mode 100644 index b7843727df..0000000000 --- a/modular_citadel/code/game/objects/structures/beds_chairs/chair.dm +++ /dev/null @@ -1,21 +0,0 @@ -/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, "You can't do that right now!") - return TRUE - if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) - to_chat(user, "You're too exhausted for that.") - return TRUE - var/mob/living/poordude = buckled_mobs[1] - if(!istype(poordude)) - return TRUE - user.visible_message("[user] pulls [src] out from under [poordude].", "You pull [src] out from under [poordude].") - 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 diff --git a/modular_citadel/code/game/objects/structures/tables_racks.dm b/modular_citadel/code/game/objects/structures/tables_racks.dm deleted file mode 100644 index 69dbe4e1cb..0000000000 --- a/modular_citadel/code/game/objects/structures/tables_racks.dm +++ /dev/null @@ -1,10 +0,0 @@ -/obj/structure/table/alt_attack_hand(mob/user) - if(user && Adjacent(user) && !user.incapacitated()) - if(istype(user) && user.a_intent == INTENT_HARM) - user.visible_message("[user] slams [user.p_their()] palms down on [src].", "You slam your palms down on [src].") - playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1) - else - user.visible_message("[user] slaps [user.p_their()] hands on [src].", "You slap your hands on [src].") - playsound(src, 'sound/weapons/tap.ogg', 50, 1) - user.do_attack_animation(src) - return TRUE diff --git a/modular_citadel/icons/mob/widerobot.dmi b/modular_citadel/icons/mob/widerobot.dmi index 7e9da953c9..43a6863ece 100644 Binary files a/modular_citadel/icons/mob/widerobot.dmi and b/modular_citadel/icons/mob/widerobot.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 33a4251dd7..f52892fce3 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -229,6 +229,7 @@ #include "code\controllers\configuration\entries\dbconfig.dm" #include "code\controllers\configuration\entries\donator.dm" #include "code\controllers\configuration\entries\dynamic.dm" +#include "code\controllers\configuration\entries\fail2topic.dm" #include "code\controllers\configuration\entries\game_options.dm" #include "code\controllers\configuration\entries\general.dm" #include "code\controllers\subsystem\acid.dm" @@ -245,6 +246,7 @@ #include "code\controllers\subsystem\dcs.dm" #include "code\controllers\subsystem\disease.dm" #include "code\controllers\subsystem\events.dm" +#include "code\controllers\subsystem\fail2topic.dm" #include "code\controllers\subsystem\fire_burning.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\icon_smooth.dm" @@ -471,6 +473,7 @@ #include "code\datums\elements\cleaning.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\ghost_role_eligibility.dm" +#include "code\datums\elements\wuv.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\getrev.dm" #include "code\datums\helper_datums\icon_snapshot.dm" @@ -1441,6 +1444,7 @@ #include "code\modules\assembly\igniter.dm" #include "code\modules\assembly\infrared.dm" #include "code\modules\assembly\mousetrap.dm" +#include "code\modules\assembly\playback.dm" #include "code\modules\assembly\proximity.dm" #include "code\modules\assembly\shock_kit.dm" #include "code\modules\assembly\signaler.dm" @@ -2030,6 +2034,7 @@ #include "code\modules\mining\mint.dm" #include "code\modules\mining\money_bag.dm" #include "code\modules\mining\ores_coins.dm" +#include "code\modules\mining\point_bank.dm" #include "code\modules\mining\satchel_ore_boxdm.dm" #include "code\modules\mining\shelters.dm" #include "code\modules\mining\equipment\explorer_gear.dm" @@ -3066,8 +3071,6 @@ #include "modular_citadel\code\datums\status_effects\chems.dm" #include "modular_citadel\code\datums\status_effects\debuffs.dm" #include "modular_citadel\code\datums\wires\autoylathe.dm" -#include "modular_citadel\code\game\machinery\displaycases.dm" -#include "modular_citadel\code\game\machinery\Sleeper.dm" #include "modular_citadel\code\game\machinery\toylathe.dm" #include "modular_citadel\code\game\machinery\vending.dm" #include "modular_citadel\code\game\machinery\wishgranter.dm" @@ -3082,8 +3085,6 @@ #include "modular_citadel\code\game\objects\items\devices\radio\encryptionkey.dm" #include "modular_citadel\code\game\objects\items\devices\radio\headset.dm" #include "modular_citadel\code\game\objects\items\devices\radio\shockcollar.dm" -#include "modular_citadel\code\game\objects\structures\tables_racks.dm" -#include "modular_citadel\code\game\objects\structures\beds_chairs\chair.dm" #include "modular_citadel\code\modules\admin\admin.dm" #include "modular_citadel\code\modules\admin\chat_commands.dm" #include "modular_citadel\code\modules\admin\holder2.dm" diff --git a/tgui/src/interfaces/point_bank.ract b/tgui/src/interfaces/point_bank.ract new file mode 100644 index 0000000000..837a66427a --- /dev/null +++ b/tgui/src/interfaces/point_bank.ract @@ -0,0 +1,60 @@ + + + Current stored points: {{data.totalPoints}} +
+
+ + -All + +
+
+ + -1000 + +
+
+ + -100 + +
+
+ + -10 + +
+
+ + -1 + +
+
+ Transfer Points +
+
+ + +1 + +
+
+ + +10 + +
+
+ + +100 + +
+
+ + +1000 + +
+
+ + +All + +
+
+
+