diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index d000508fdeb..933cf12dcfc 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -242,6 +242,9 @@ #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; +// Locations +#define is_ventcrawling(A) (istype(A.loc, /obj/machinery/atmospherics)) + // Hearing protection #define HEARING_PROTECTION_NONE 0 #define HEARING_PROTECTION_MINOR 1 diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 50c5314d0b4..03d34395479 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -9,7 +9,7 @@ Note that AI have no need for the adjacency proc, and so this proc is a lot cleaner. */ -/mob/living/silicon/ai/DblClickOn(var/atom/A, params) +/mob/living/silicon/ai/DblClickOn(atom/A, params) if(client.click_intercept) // Not doing a click intercept here, because otherwise we double-tap with the `ClickOn` proc. // But we return here since we don't want to do regular dblclick handling @@ -23,7 +23,7 @@ A.move_camera_by_click() -/mob/living/silicon/ai/ClickOn(var/atom/A, params) +/mob/living/silicon/ai/ClickOn(atom/A, params) if(client.click_intercept) client.click_intercept.InterceptClickOn(src, params, A) return @@ -123,7 +123,7 @@ /mob/living/silicon/ai/RangedAttack(atom/A, params) A.attack_ai(src) -/atom/proc/attack_ai(mob/user as mob) +/atom/proc/attack_ai(mob/user) return /* @@ -132,17 +132,17 @@ for AI shift, ctrl, and alt clicking. */ -/mob/living/silicon/ai/CtrlShiftClickOn(var/atom/A) +/mob/living/silicon/ai/CtrlShiftClickOn(atom/A) A.AICtrlShiftClick(src) -/mob/living/silicon/ai/AltShiftClickOn(var/atom/A) +/mob/living/silicon/ai/AltShiftClickOn(atom/A) A.AIAltShiftClick(src) -/mob/living/silicon/ai/ShiftClickOn(var/atom/A) +/mob/living/silicon/ai/ShiftClickOn(atom/A) A.AIShiftClick(src) -/mob/living/silicon/ai/CtrlClickOn(var/atom/A) +/mob/living/silicon/ai/CtrlClickOn(atom/A) A.AICtrlClick(src) -/mob/living/silicon/ai/AltClickOn(var/atom/A) +/mob/living/silicon/ai/AltClickOn(atom/A) A.AIAltClick(src) -/mob/living/silicon/ai/MiddleClickOn(var/atom/A) +/mob/living/silicon/ai/MiddleClickOn(atom/A) A.AIMiddleClick(src) @@ -224,14 +224,6 @@ return toggle_light(user) -// FIRE ALARMS - -/obj/machinery/firealarm/AICtrlClick() - if(enabled) - reset() - else - alarm() - // AI-CONTROLLED SLIP GENERATOR IN AI CORE /obj/machinery/ai_slipper/AICtrlClick(mob/living/silicon/ai/user) //Turns liquid dispenser on or off diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index f35b2cece0b..fa29e857ab7 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -6,7 +6,7 @@ adjacency code. */ -/mob/living/silicon/robot/ClickOn(var/atom/A, var/params) +/mob/living/silicon/robot/ClickOn(atom/A, params) if(client.click_intercept) client.click_intercept.InterceptClickOn(src, params, A) return @@ -15,6 +15,8 @@ return changeNext_click(1) + if(is_ventcrawling(src)) // To stop drones interacting with anything while ventcrawling + return var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["ctrl"]) @@ -98,12 +100,12 @@ return //Ctrl+Middle click cycles through modules -/mob/living/silicon/robot/proc/CtrlMiddleClickOn(var/atom/A) +/mob/living/silicon/robot/proc/CtrlMiddleClickOn(atom/A) cycle_modules() return //Middle click points -/mob/living/silicon/robot/MiddleClickOn(var/atom/A) +/mob/living/silicon/robot/MiddleClickOn(atom/A) if(istype(src, /mob/living/silicon/robot/drone)) // Drones cannot point. return @@ -112,18 +114,31 @@ //Give cyborgs hotkey clicks without breaking existing uses of hotkey clicks // for non-doors/apcs -/mob/living/silicon/robot/CtrlShiftClickOn(var/atom/A) - A.BorgCtrlShiftClick(src) -/mob/living/silicon/robot/AltShiftClickOn(var/atom/A) - A.BorgAltShiftClick(src) -/mob/living/silicon/robot/ShiftClickOn(var/atom/A) +/mob/living/silicon/robot/ShiftClickOn(atom/A) A.BorgShiftClick(src) -/mob/living/silicon/robot/CtrlClickOn(var/atom/A) +/mob/living/silicon/robot/CtrlClickOn(atom/A) A.BorgCtrlClick(src) -/mob/living/silicon/robot/AltClickOn(var/atom/A) +/mob/living/silicon/robot/AltClickOn(atom/A) A.BorgAltClick(src) +/mob/living/silicon/robot/CtrlShiftClickOn(atom/A) + A.BorgCtrlShiftClick(src) +/mob/living/silicon/robot/AltShiftClickOn(atom/A) + A.BorgAltShiftClick(src) -/atom/proc/BorgCtrlShiftClick(var/mob/user) // Examines + +/atom/proc/BorgShiftClick(mob/user) + if(user.client && user.client.eye == user) + user.examinate(src) + return + +/atom/proc/BorgCtrlClick(mob/living/silicon/robot/user) //forward to human click if not overriden + CtrlClick(user) + +/atom/proc/BorgAltClick(mob/living/silicon/robot/user) + AltClick(user) + return + +/atom/proc/BorgCtrlShiftClick(mob/user) // Examines if(user.client && user.client.eye == user) user.examinate(src) return @@ -131,38 +146,29 @@ /atom/proc/BorgAltShiftClick() return -/obj/machinery/door/airlock/BorgAltShiftClick(mob/living/silicon/robot/user) // Enables emergency override on doors! Forwards to AI code. - AIAltShiftClick(user) -/atom/proc/BorgShiftClick(var/mob/user) - if(user.client && user.client.eye == user) - user.examinate(src) - return +// AIRLOCKS /obj/machinery/door/airlock/BorgShiftClick(mob/living/silicon/robot/user) // Opens and closes doors! Forwards to AI code. AIShiftClick(user) -/atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden - CtrlClick(user) - /obj/machinery/door/airlock/BorgCtrlClick(mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. AICtrlClick(user) -/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. - AICtrlClick(user) - -/obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. - AICtrlClick(user) - -/atom/proc/BorgAltClick(var/mob/living/silicon/robot/user) - AltClick(user) - return - /obj/machinery/door/airlock/BorgAltClick(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. AIAltClick(user) -/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. - AIAltClick(user) +/obj/machinery/door/airlock/BorgAltShiftClick(mob/living/silicon/robot/user) // Enables emergency override on doors! Forwards to AI code. + AIAltShiftClick(user) + + +// APC + +/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. + AICtrlClick(user) + + +// AI SLIPPER /obj/machinery/ai_slipper/BorgCtrlClick(mob/living/silicon/robot/user) //Turns liquid dispenser on or off ToggleOn() @@ -170,9 +176,15 @@ /obj/machinery/ai_slipper/BorgAltClick(mob/living/silicon/robot/user) //Dispenses liquid if on Activate() -/obj/machinery/firealarm/BorgCtrlClick(mob/living/silicon/robot/user) + +// TURRETCONTROL + +/obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. AICtrlClick(user) +/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. + AIAltClick(user) + /* As with AI, these are not used in click code, because the code for robots is specific, not generic. @@ -187,6 +199,6 @@ /mob/living/silicon/robot/RangedAttack(atom/A, params) A.attack_robot(src) -/atom/proc/attack_robot(mob/user as mob) +/atom/proc/attack_robot(mob/user) attack_ai(user) return diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index d063b742047..e099ff0b384 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -41,9 +41,16 @@ var/tech_fluff_string = "BOOT SEQUENCE COMPLETE. ERROR MODULE LOADED. THIS SHOULDN'T HAPPEN. Submit a bug report!" var/bio_fluff_string = "Your scarabs fail to mutate. This shouldn't happen! Submit a bug report!" var/admin_fluff_string = "URK URF!"//the wheels on the bus... - var/adminseal = FALSE var/name_color = "white"//only used with protector shields for the time being +/mob/living/simple_animal/hostile/guardian/Initialize(mapload, mob/living/host) + . = ..() + if(!host) + return + summoner = host + host.grant_guardian_actions(src) + RegisterSignal(host, COMSIG_MOB_DEATH, .proc/on_host_death) + /mob/living/simple_animal/hostile/guardian/med_hud_set_health() if(summoner) var/image/holder = hud_list[HEALTH_HUD] @@ -59,19 +66,26 @@ else holder.icon_state = "hudhealthy" -/mob/living/simple_animal/hostile/guardian/Life(seconds, times_fired) //Dies if the summoner dies +/** + * Proc which is called when the guardian's host dies. + * + * This will only fire if the guardian was created through a holoparsite injector, or the equivalent. + */ +/mob/living/simple_animal/hostile/guardian/proc/on_host_death() + if(summoner.stat == DEAD || (!summoner.check_death_method() && summoner.health <= HEALTH_THRESHOLD_DEAD)) + summoner.remove_guardian_actions() // Remove our summoner's action buttons. + to_chat(src, "Your summoner has died!") + visible_message("[src] dies along with its user!") + ghostize() + qdel(src) + +/mob/living/simple_animal/hostile/guardian/Life(seconds, times_fired) ..() - if(summoner) - if(summoner.stat == DEAD || (!summoner.check_death_method() && summoner.health <= HEALTH_THRESHOLD_DEAD)) - to_chat(src, "Your summoner has died!") - visible_message("[src] dies along with its user!") - ghostize() - qdel(src) snapback() - if(summoned && !summoner && !adminseal) + if(summoned && !summoner && !admin_spawned) to_chat(src, "You somehow lack a summoner! As a result, you dispel!") ghostize() - qdel() + qdel(src) /mob/living/simple_animal/hostile/guardian/proc/snapback() // If the summoner dies instantly, the summoner's ghost may be drawn into null space as the protector is deleted. This check should prevent that. @@ -197,7 +211,7 @@ //override set to true if message should be passed through instead of going to host communication /mob/living/simple_animal/hostile/guardian/say(message, override = FALSE) - if(adminseal || override)//if it's an admin-spawned guardian without a host it can still talk normally + if(admin_spawned || override)//if it's an admin-spawned guardian without a host it can still talk normally return ..(message) Communicate(message) @@ -206,61 +220,6 @@ to_chat(src, "You dont have another mode!") -/mob/living/proc/guardian_comm() - set name = "Communicate" - set category = "Guardian" - set desc = "Communicate telepathically with your guardian." - var/input = stripped_input(src, "Please enter a message to tell your guardian.", "Message", "") - if(!input) - return - - // Find the guardian in our host's contents. - var/mob/living/simple_animal/hostile/guardian/G = locate() in contents - if(!G) - return - - // Show the message to our guardian and to host. - to_chat(G, "[src]: [input]") - to_chat(src, "[src]: [input]") - log_say("(GUARDIAN to [key_name(G)]) [input]", src) - create_log(SAY_LOG, "HOST to GUARDIAN: [input]", G) - - // Show the message to any ghosts/dead players. - for(var/mob/M in GLOB.dead_mob_list) - if(M && M.client && M.stat == DEAD && !isnewplayer(M)) - to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]") - -/mob/living/proc/guardian_recall() - set name = "Recall Guardian" - set category = "Guardian" - set desc = "Forcibly recall your guardian." - for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list) - if(G.summoner == src) - G.Recall() - -/mob/living/proc/guardian_reset() - set name = "Reset Guardian Player (One Use)" - set category = "Guardian" - set desc = "Re-rolls which ghost will control your Guardian. One use." - - src.verbs -= /mob/living/proc/guardian_reset - for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list) - if(G.summoner == src) - var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G) - var/mob/dead/observer/new_stand = null - if(candidates.len) - new_stand = pick(candidates) - to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") - to_chat(src, "Your guardian has been successfully reset.") - message_admins("[key_name_admin(new_stand)] has taken control of ([key_name_admin(G)])") - G.ghostize() - G.key = new_stand.key - else - to_chat(src, "There were no ghosts willing to take control. Looks like you're stuck with your Guardian for now.") - spawn(3000) - verbs += /mob/living/proc/guardian_reset - - /mob/living/simple_animal/hostile/guardian/proc/ToggleLight() if(!light_on) set_light(luminosity_on) @@ -364,7 +323,6 @@ pickedtype = /mob/living/simple_animal/hostile/guardian/protector var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user, user) - G.summoner = user G.summoned = TRUE G.key = key to_chat(G, "You are a [mob_name] bound to serve [user.real_name].") @@ -372,9 +330,6 @@ to_chat(G, "While personally invincible, you will die if [user.real_name] does, and any damage dealt to you will have a portion passed on to them as you feed upon them to sustain yourself.") to_chat(G, "[G.playstyle_string]") G.faction = user.faction - user.verbs += /mob/living/proc/guardian_comm - user.verbs += /mob/living/proc/guardian_recall - user.verbs += /mob/living/proc/guardian_reset var/color = pick(color_list) G.name_color = color_list[color] diff --git a/code/game/gamemodes/miniantags/guardian/host_actions.dm b/code/game/gamemodes/miniantags/guardian/host_actions.dm new file mode 100644 index 00000000000..c6e48221705 --- /dev/null +++ b/code/game/gamemodes/miniantags/guardian/host_actions.dm @@ -0,0 +1,130 @@ +/** + * # Base guardian host action + * + * These are used by guardian hosts to interact with their guardians. These are not buttons that guardians themselves use. + */ +/datum/action/guardian + name = "Generic guardian host action" + icon_icon = 'icons/mob/guardian.dmi' + button_icon_state = "base" + var/mob/living/simple_animal/hostile/guardian/guardian + +/datum/action/guardian/Grant(mob/M, mob/living/simple_animal/hostile/guardian/G) + if(!G || !istype(G)) + stack_trace("/datum/action/guardian created with no guardian to link to.") + qdel(src) + guardian = G + return ..() + +/** + * # Communicate action + * + * Allows the guardian host to communicate with their guardian. + */ +/datum/action/guardian/communicate + name = "Communicate" + desc = "Communicate telepathically with your guardian." + button_icon_state = "communicate" + +/datum/action/guardian/communicate/Trigger() + var/input = stripped_input(owner, "Enter a message to tell your guardian:", "Message", "") + if(!input) + return + + // Show the message to our guardian and to host. + to_chat(guardian, "[owner]: [input]") + to_chat(owner, "[owner]: [input]") + log_say("(GUARDIAN to [key_name(guardian)]) [input]", owner) + owner.create_log(SAY_LOG, "HOST to GUARDIAN: [input]", guardian) + + // Show the message to any ghosts/dead players. + for(var/mob/M in GLOB.dead_mob_list) + if(M && M.client && M.stat == DEAD && !isnewplayer(M)) + to_chat(M, "Guardian Communication from [owner] ([ghost_follow_link(owner, ghost=M)]): [input]") + +/** + * # Recall guardian action + * + * Allows the guardian host to recall their guardian. + */ +/datum/action/guardian/recall + name = "Recall Guardian" + desc = "Forcibly recall your guardian." + button_icon_state = "recall" + +/datum/action/guardian/recall/Trigger() + guardian.Recall() + +/** + * # Reset guardian action + * + * Allows the guardian host to exchange their guardian's player for another. + */ +/datum/action/guardian/reset_guardian + name = "Replace Guardian Player" + desc = "Replace your guardian's player with a ghost. This can only be done once." + button_icon_state = "reset" + var/cooldown_timer + +/datum/action/guardian/reset_guardian/IsAvailable() + if(cooldown_timer) + return FALSE + return TRUE + +/datum/action/guardian/reset_guardian/Trigger() + if(cooldown_timer) + to_chat(owner, "This ability is still recharging.") + return + + var/confirm = alert("Are you sure you want replace your guardian's player?", "Confirm", "Yes", "No") + if(confirm == "No") + return + + // Do this immediately, so the user can't spam a bunch of polls. + cooldown_timer = addtimer(CALLBACK(src, .proc/reset_cooldown), 5 MINUTES) + UpdateButtonIcon() + + to_chat(owner, "Searching for a replacement ghost...") + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [guardian.real_name]?", ROLE_GUARDIAN, FALSE, 15 SECONDS, source = guardian) + + if(!length(candidates)) + to_chat(owner, "There were no ghosts willing to take control of your guardian. You can try again in 5 minutes.") + return + + var/mob/dead/observer/new_stand = pick(candidates) + to_chat(guardian, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") + to_chat(owner, "Your guardian has been successfully reset.") + message_admins("[key_name_admin(new_stand)] has taken control of ([key_name_admin(guardian)])") + guardian.ghostize() + guardian.key = new_stand.key + qdel(src) + +/** + * Takes the action button off cooldown and makes it available again. + */ +/datum/action/guardian/reset_guardian/proc/reset_cooldown() + cooldown_timer = null + UpdateButtonIcon() + +/** + * Grants all existing `/datum/action/guardian` type actions to the src mob. + * + * Called whenever the host gains their gauardian. + */ +/mob/living/proc/grant_guardian_actions(mob/living/simple_animal/hostile/guardian/G) + if(!G || !istype(G)) + return + for(var/action in subtypesof(/datum/action/guardian)) + var/datum/action/guardian/A = new action + A.Grant(src, G) + +/** + * Removes all `/datum/action/guardian` type actions from the src mob. + * + * Called whenever the host loses their guardian. + */ +/mob/living/proc/remove_guardian_actions() + for(var/action in actions) + var/datum/action/A = action + if(istype(A, /datum/action/guardian)) + A.Remove(src) diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm index c3f8046f0a9..72d1a03301e 100644 --- a/code/game/gamemodes/miniantags/guardian/types/healer.dm +++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm @@ -26,7 +26,7 @@ melee_damage_lower = 0 melee_damage_upper = 0 melee_damage_type = STAMINA - adminseal = TRUE + admin_spawned = TRUE /mob/living/simple_animal/hostile/guardian/healer/New() ..() @@ -68,7 +68,7 @@ hud_used.action_intent.icon_state = a_intent speed = 0 damage_transfer = 0.7 - if(adminseal) + if(admin_spawned) damage_transfer = 0 melee_damage_lower = 15 melee_damage_upper = 15 @@ -79,7 +79,7 @@ hud_used.action_intent.icon_state = a_intent speed = 1 damage_transfer = 1 - if(adminseal) + if(admin_spawned) damage_transfer = 0 melee_damage_lower = 0 melee_damage_upper = 0 diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm index 2c716d5d4a4..e2ab6360ae5 100644 --- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm +++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm @@ -18,13 +18,12 @@ var/list/enemychains = list() var/successfulshocks = 0 -/mob/living/simple_animal/hostile/guardian/beam/New(loc, mob/living/user) - . = ..() - if(!user) - return - summoner = user - if(!(NO_SHOCK in summoner.mutations)) - summoner.mutations.Add(NO_SHOCK) +/mob/living/simple_animal/hostile/guardian/beam/Initialize(mapload, mob/living/host) + . = ..() + if(!summoner) + return + if(!(NO_SHOCK in summoner.mutations)) + summoner.mutations.Add(NO_SHOCK) /mob/living/simple_animal/hostile/guardian/beam/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) return FALSE //You are lightning, you should not be hurt by such things. diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index e6d95ffb92c..d458ce2f49d 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -45,4 +45,4 @@ playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." environment_smash = 2 battlecry = "URK" - adminseal = TRUE + admin_spawned = TRUE diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index ea273850d90..b950f3d51a3 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -367,10 +367,8 @@ update_flag if("eject") if(holding) if(valve_open) - if(air_contents && (air_contents.toxins > 0 || air_contents.sleeping_agent > 0)) - message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the air.") - release_log += "[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air
" - investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air.", "atmos") + valve_open = FALSE + release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
" replace_tank(usr, FALSE) if("recolor") if(can_label) diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 6144d1c3883..137dcb50110 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -32,7 +32,6 @@ FIRE ALARM var/wiresexposed = 0 var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone - var/enabled = FALSE var/report_fire_alarms = TRUE // Should triggered fire alarms also trigger an actual alarm? var/show_alert_level = TRUE // Should fire alarms display the current alert level? diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 067411ed647..fe15d146aa4 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -183,6 +183,82 @@ return TRUE +/obj/item/borg/upgrade/abductor_engi + name = "engineering cyborg abductor upgrade" + desc = "An experimental upgrade that replaces an engineering cyborgs tools with the abductor version." + icon_state = "abductor_mod" + origin_tech = "engineering=6;materials=6;abductor=3" + require_module = TRUE + module_type = /obj/item/robot_module/engineering + +/obj/item/borg/upgrade/abductor_engi/action(mob/living/silicon/robot/R) + if(..()) + return + + for(var/obj/item/weldingtool/largetank/cyborg/W in R.module.modules) + qdel(W) + for(var/obj/item/screwdriver/cyborg/S in R.module.modules) + qdel(S) + for(var/obj/item/wrench/cyborg/E in R.module.modules) + qdel(E) + for(var/obj/item/crowbar/cyborg/C in R.module.modules) + qdel(C) + for(var/obj/item/wirecutters/cyborg/I in R.module.modules) + qdel(I) + for(var/obj/item/multitool/cyborg/M in R.module.modules) + qdel(M) + + R.module.modules += new /obj/item/weldingtool/abductor(R.module) + R.module.modules += new /obj/item/wrench/abductor(R.module) + R.module.modules += new /obj/item/screwdriver/abductor(R.module) + R.module.modules += new /obj/item/crowbar/abductor(R.module) + R.module.modules += new /obj/item/wirecutters/abductor(R.module) + R.module.modules += new /obj/item/multitool/abductor(R.module) + R.module.rebuild() + + return TRUE + +/obj/item/borg/upgrade/abductor_medi + name = "medical cyborg abductor upgrade" + desc = "An experimental upgrade that replaces a medical cyborgs tools with the abductor version." + icon_state = "abductor_mod" + origin_tech = "biotech=6;materials=6;abductor=3" + require_module = TRUE + module_type = /obj/item/robot_module/medical + +/obj/item/borg/upgrade/abductor_medi/action(mob/living/silicon/robot/R) + if(..()) + return + + for(var/obj/item/scalpel/laser/laser1/L in R.module.modules) + qdel(L) + for(var/obj/item/hemostat/H in R.module.modules) + qdel(H) + for(var/obj/item/retractor/E in R.module.modules) + qdel(E) + for(var/obj/item/bonegel/B in R.module.modules) + qdel(B) + for(var/obj/item/FixOVein/F in R.module.modules) + qdel(F) + for(var/obj/item/bonesetter/S in R.module.modules) + qdel(S) + for(var/obj/item/circular_saw/C in R.module.modules) + qdel(C) + for(var/obj/item/surgicaldrill/D in R.module.modules) + qdel(D) + + R.module.modules += new /obj/item/scalpel/laser/laser3(R.module) //no abductor laser scalpel, so next best thing. + R.module.modules += new /obj/item/hemostat/alien(R.module) + R.module.modules += new /obj/item/retractor/alien(R.module) + R.module.modules += new /obj/item/bonegel/alien(R.module) + R.module.modules += new /obj/item/FixOVein/alien(R.module) + R.module.modules += new /obj/item/bonesetter/alien(R.module) + R.module.modules += new /obj/item/circular_saw/alien(R.module) + R.module.modules += new /obj/item/surgicaldrill/alien(R.module) + R.module.rebuild() + + return TRUE + /obj/item/borg/upgrade/syndicate name = "safety override module" desc = "Unlocks the hidden, deadlier functions of a cyborg." diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index 121782b2e8e..5370004a0a7 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -31,7 +31,6 @@ desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" icon = 'icons/obj/janitor.dmi' icon_state = "trashbag" - item_state = "trashbag" w_class = WEIGHT_CLASS_BULKY max_w_class = WEIGHT_CLASS_SMALL @@ -48,14 +47,19 @@ /obj/item/storage/bag/trash/update_icon() switch(contents.len) - if(20 to INFINITY) + if(21 to INFINITY) icon_state = "[initial(icon_state)]3" if(11 to 20) icon_state = "[initial(icon_state)]2" - if(1 to 11) + if(1 to 10) icon_state = "[initial(icon_state)]1" else icon_state = "[initial(icon_state)]" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + H.update_inv_l_hand() + H.update_inv_r_hand() + ..() /obj/item/storage/bag/trash/cyborg diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 138c77f92eb..d1d26b7a2bb 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -234,7 +234,7 @@ add_fingerprint(user) /obj/structure/closet/attack_ai(mob/user) - if(isrobot(user) && Adjacent(user) && !istype(user.loc, /obj/machinery/atmospherics)) //Robots can open/close it, but not the AI + if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI attack_hand(user) /obj/structure/closet/relaymove(mob/user) diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 63f5cf8dca3..f90b1703d34 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -312,6 +312,22 @@ flags = BLOCKHAIR flags_inv = HIDEEARS +/obj/item/clothing/suit/hooded/salmon_costume + name = "salmon suit" + desc = "A costume made from authentic salmon scales, it reeks!" + icon_state = "salmon" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list(/obj/item/fish/salmon, /obj/item/fish_eggs/salmon) + hoodtype = /obj/item/clothing/head/hooded/salmon_hood + +/obj/item/clothing/head/hooded/salmon_hood + name = "salmon hood" + desc = "A hood attached to a salmon suit." + icon_state = "salmon" + body_parts_covered = HEAD + flags = BLOCKHAIR + flags_inv = HIDEEARS + /obj/item/clothing/suit/hooded/bee_costume // It's Hip! name = "bee costume" desc = "Bee the true Queen!" diff --git a/code/modules/crafting/tailoring.dm b/code/modules/crafting/tailoring.dm index 38419f718c4..68fa7fca3f3 100644 --- a/code/modules/crafting/tailoring.dm +++ b/code/modules/crafting/tailoring.dm @@ -152,3 +152,13 @@ /obj/item/clothing/shoes/sandal = 1) tools = list(TOOL_WIRECUTTER) category = CAT_CLOTHING + +/datum/crafting_recipe/salmonsuit + name = "Salmon Suit" + result = /obj/item/clothing/suit/hooded/salmon_costume + time = 60 + reqs = list(/obj/item/fish/salmon = 20, + /obj/item/stack/tape_roll = 5) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/kitchen/knife) + category = CAT_CLOTHING diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index ece9c743854..508dd271849 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -9,6 +9,7 @@ return FALSE /mob/proc/death(gibbed) + SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) return FALSE /mob/proc/dust_animation() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index b6e93dc78ff..a496526008f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -514,10 +514,10 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven /mob/living/update_pipe_vision() if(pipes_shown.len) - if(!istype(loc, /obj/machinery/atmospherics)) + if(!is_ventcrawling(src)) remove_ventcrawl() else - if(istype(loc, /obj/machinery/atmospherics)) + if(is_ventcrawling(src)) add_ventcrawl(loc) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6af90792355..09cc812eae3 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -51,6 +51,7 @@ // Whew! Good thing I'm indestructible! (or already dead) return FALSE + ..() stat = DEAD SetDizzy(0) SetJitter(0) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index ff967354131..25d9a0decb8 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -74,7 +74,7 @@ var/list/temp_list = modules modules = list() for(var/obj/O in temp_list) - if(O) + if(!QDELETED(O)) //so items getting deleted don't stay in module list and haunt you modules += O /obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index d3f5f76303c..6146cd3ac2c 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -311,15 +311,6 @@ /obj/machinery/power/supermatter_shard/attack_robot(mob/user as mob) if(Adjacent(user)) return attack_hand(user) - else - ui_interact(user) - return - -/obj/machinery/power/supermatter_shard/attack_ai(mob/user as mob) - ui_interact(user) - -/obj/machinery/power/supermatter_shard/attack_ghost(mob/user as mob) - ui_interact(user) /obj/machinery/power/supermatter_shard/attack_hand(mob/user as mob) user.visible_message("\The [user] reaches out and touches \the [src], inducing a resonance... [user.p_their(TRUE)] body starts to glow and bursts into flames before flashing into ash.",\ @@ -336,35 +327,6 @@ integrity = integrity < 0 ? 0 : integrity return integrity -// This is purely informational UI that may be accessed by AIs or robots -/obj/machinery/power/supermatter_shard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "supermatter_crystal.tmpl", "Supermatter Crystal", 500, 300) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/supermatter_shard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["integrity_percentage"] = round(get_integrity()) - var/datum/gas_mixture/env = null - if(!istype(src.loc, /turf/space)) - env = src.loc.return_air() - - if(!env) - data["ambient_temp"] = 0 - data["ambient_pressure"] = 0 - else - data["ambient_temp"] = round(env.temperature) - data["ambient_pressure"] = round(env.return_pressure()) - if(damage > explosion_point) - data["detonating"] = 1 - else - data["detonating"] = 0 - - return data - /obj/machinery/power/supermatter_shard/proc/transfer_energy() for(var/obj/machinery/power/rad_collector/R in GLOB.rad_collectors) if(get_dist(R, src) <= 15) // Better than using orange() every process diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index d9b20c9d075..36faa49b533 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -185,7 +185,8 @@ /obj/structure/sign, /obj/machinery/gateway, /obj/structure/grille, - /obj/machinery/the_singularitygen/tesla)) + /obj/machinery/the_singularitygen/tesla, + /mob/living/simple_animal/slime)) for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+2), things_to_shock, blacklisted_tesla_types)) diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index 914fabbc752..7156eff6482 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -917,6 +917,7 @@ reagent_state = LIQUID color = "#191919" metabolization_rate = 0.1 + can_synth = FALSE penetrates_skin = TRUE taste_mult = 0 diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 7bd5b3e9c93..22a5c7494bb 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1081,6 +1081,26 @@ construction_time = 120 category = list("Cyborg Upgrade Modules") +/datum/design/borg_upgrade_abductor_engi + name = "Cyborg Upgrade (Abdcutor Engineering Equipment)" + id = "borg_upgade_abductor_engi" + build_type = MECHFAB + build_path = /obj/item/borg/upgrade/abductor_engi + req_tech = list("engineering" = 7, "materials" = 7, "abductor" = 4) + materials = list(MAT_METAL = 25000, MAT_SILVER = 12500, MAT_PLASMA = 5000, MAT_TITANIUM = 10000, MAT_DIAMOND = 10000) //Base abductor engineering tools * 4 + construction_time = 120 + category = list("Cyborg Upgrade Modules") + +/datum/design/borg_upgrade_abductor_medi + name = "Cyborg Upgrade (Abdcutor Medical Equipment)" + id = "borg_upgade_abductor_medi" + build_type = MECHFAB + build_path = /obj/item/borg/upgrade/abductor_medi + req_tech = list("biotech" = 7, "materials" = 7, "abductor" = 4) + materials = list(MAT_METAL = 18000, MAT_GLASS = 1500, MAT_SILVER = 13000, MAT_GOLD = 1000, MAT_PLASMA = 4000, MAT_TITANIUM = 12000, MAT_DIAMOND = 1000) //Base abductor engineering tools *8 + IMS cost + construction_time = 120 + category = list("Cyborg Upgrade Modules") + /datum/design/borg_upgrade_lavaproof name = "Cyborg Upgrade (Lavaproof Chassis)" id = "borg_upgrade_lavaproof" diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index 9b7ba72209b..0d9acc07207 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -16,12 +16,14 @@ GLOBAL_LIST_EMPTY(GPS_list) /obj/item/gps/New() ..() GLOB.GPS_list.Add(src) + GLOB.poi_list.Add(src) if(name == "default gps") //use default naming scheme name = "global positioning system ([gpstag])" overlays += "working" /obj/item/gps/Destroy() GLOB.GPS_list.Remove(src) + GLOB.poi_list.Remove(src) return ..() /obj/item/gps/emp_act(severity) diff --git a/icons/mob/guardian.dmi b/icons/mob/guardian.dmi index a10e1e0b0ec..e10a5c618ea 100644 Binary files a/icons/mob/guardian.dmi and b/icons/mob/guardian.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index ddc5f3cf05c..5221aec971f 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index ddc012075f6..61224892056 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index b13e2c3307d..34a1864257b 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 12a0a371a94..c7df6f275aa 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 30f34f01b4f..2418b096381 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 62fc0a6120c..771b784f6cd 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi index 730431590ae..ebc1cca56da 100644 Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ diff --git a/nano/templates/supermatter_crystal.tmpl b/nano/templates/supermatter_crystal.tmpl deleted file mode 100644 index 40bc6796cd7..00000000000 --- a/nano/templates/supermatter_crystal.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -{{if data.detonating}} -
-

CRYSTAL DELAMINATING

-

Evacuate area immediately

-
-
-{{else}} -

Crystal Integrity

- {{:helper.displayBar(data.integrity_percentage, 0, 100, (data.integrity_percentage >= 90) ? 'good' : (data.integrity_percentage >= 25) ? 'average' : 'bad')}} - {{:data.integrity_percentage}} % -

Environment

- - Temperature: - - - {{:helper.displayBar(data.ambient_temp, 0, 10000, (data.ambient_temp >= 5000) ? 'bad' : (data.ambient_temp >= 4000) ? 'average' : 'good')}} - {{:data.ambient_temp}} K - - - Pressure: - - - {{:helper.smoothRound(data.ambient_pressure)}} kPa - -{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index efddfaea144..23e566ede1a 100644 --- a/paradise.dme +++ b/paradise.dme @@ -575,6 +575,7 @@ #include "code\game\gamemodes\miniantags\bot_swarm\swarmer.dm" #include "code\game\gamemodes\miniantags\bot_swarm\swarmer_event.dm" #include "code\game\gamemodes\miniantags\guardian\guardian.dm" +#include "code\game\gamemodes\miniantags\guardian\host_actions.dm" #include "code\game\gamemodes\miniantags\guardian\types\assassin.dm" #include "code\game\gamemodes\miniantags\guardian\types\bomb.dm" #include "code\game\gamemodes\miniantags\guardian\types\charger.dm" diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 67e74085c7e..595a6978f91 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -366,13 +366,13 @@ rules: ## Enforce a maximum depth that blocks can be nested # max-depth: error ## Enforce a maximum line length - max-len: [error, { - code: 80, + #max-len: [error, { + # code: 80, ## Ignore imports - ignorePattern: '^(import\s.+\sfrom\s|.*require\()', - ignoreUrls: true, - ignoreRegExpLiterals: true, - }] + #ignorePattern: '^(import\s.+\sfrom\s|.*require\()', + #ignoreUrls: true, + #ignoreRegExpLiterals: true, + #}] ## Enforce a maximum number of lines per file # max-lines: error ## Enforce a maximum number of line of code in a function diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.js index 8c184f71e68..621637b4cc2 100644 --- a/tgui/packages/tgui/interfaces/Canister.js +++ b/tgui/packages/tgui/interfaces/Canister.js @@ -221,7 +221,6 @@ export const Canister = (props, context) => { buttons={!!hasHoldingTank && (