diff --git a/code/modules/client/preferences/mod_select.dm b/code/modules/client/preferences/mod_select.dm index c80c8b4c694..ac3daf2250c 100644 --- a/code/modules/client/preferences/mod_select.dm +++ b/code/modules/client/preferences/mod_select.dm @@ -19,5 +19,5 @@ var/obj/item/mod/control/mod = client_owner.back if(!mod.selected_module) return - UnregisterSignal(mod.wearer, mod.selected_module.used_signal) + mod.selected_module.UnregisterSignal(mod.wearer, mod.selected_module.used_signal) mod.selected_module.update_signal(value) diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm index e3d7ba6fc5d..d6bb55ef31f 100644 --- a/code/modules/mod/mod_actions.dm +++ b/code/modules/mod/mod_actions.dm @@ -53,13 +53,32 @@ name = "Activate MODsuit" desc = "Activate/Deactivate the MODsuit." button_icon_state = "activate" + /// First time clicking this will set it to TRUE, second time will activate it. + var/ready = FALSE /datum/action/item_action/mod/activate/Trigger() . = ..() if(!.) return + if(!ready) + ready = TRUE + button_icon_state = "activate-ready" + if(!ai_action) + background_icon_state = "bg_tech" + UpdateButtonIcon() + addtimer(CALLBACK(src, .proc/reset_ready), 3 SECONDS) + return + reset_ready() mod.toggle_activate(usr) +/// Resets the state requiring to be doubleclicked again. +/datum/action/item_action/mod/activate/proc/reset_ready() + ready = FALSE + button_icon_state = initial(button_icon_state) + if(!ai_action) + background_icon_state = initial(background_icon_state) + UpdateButtonIcon() + /datum/action/item_action/mod/activate/ai ai_action = TRUE @@ -105,6 +124,8 @@ icon_icon = linked_module.icon button_icon_state = linked_module.icon_state pinner = user + RegisterSignal(mod, COMSIG_MOD_MODULE_SELECTED, .proc/on_module_select) + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation) /datum/action/item_action/mod/pinned_module/Grant(mob/user) if(user != pinner) @@ -116,3 +137,24 @@ if(!.) return module.on_select() + +/datum/action/item_action/mod/pinned_module/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force) + . = ..(current_button, force = TRUE) + if(module == mod.selected_module) + current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected", layer = FLOAT_LAYER-0.1)) + else if(module.active) + current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_active", layer = FLOAT_LAYER-0.1)) + +/// When the module is selected, we update the icon. +/datum/action/item_action/mod/pinned_module/proc/on_module_select(datum/source, obj/item/mod/module/selected_module) + SIGNAL_HANDLER + + if(selected_module != mod.selected_module && selected_module != module) + return + UpdateButtonIcon() + +/// When the suit is being activated, we update the icon. +/datum/action/item_action/mod/pinned_module/proc/on_mod_activation(datum/source, mob/user) + SIGNAL_HANDLER + + UpdateButtonIcon() diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index 21f9c667897..acef93763c8 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -85,7 +85,7 @@ /obj/item/mod/module/proc/on_unequip() return -/// Called when the module is selected from the TGUI +/// Called when the module is selected from the TGUI, radial or the action button /obj/item/mod/module/proc/on_select() if(!mod.active || mod.activating || module_type == MODULE_PASSIVE) return @@ -96,7 +96,7 @@ on_activation() else on_use() - SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED) + SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src) /// Called when the module is activated /obj/item/mod/module/proc/on_activation() @@ -145,6 +145,7 @@ /// Called when the module is used /obj/item/mod/module/proc/on_use() if(!COOLDOWN_FINISHED(src, cooldown_timer)) + balloon_alert(mod.wearer, "on cooldown!") return FALSE if(!check_power(use_power_cost)) return FALSE diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 6ede6d28b58..91490aeaf44 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -181,15 +181,15 @@ /obj/item/mod/module/mouthhole/on_install() former_flags = mod.helmet.flags_cover former_visor_flags = mod.helmet.visor_flags_cover - if(!(former_flags & HEADCOVERSMOUTH)) + if(former_flags & HEADCOVERSMOUTH) mod.helmet.flags_cover &= ~HEADCOVERSMOUTH - if(!(former_visor_flags & HEADCOVERSMOUTH)) + if(former_visor_flags & HEADCOVERSMOUTH) mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH /obj/item/mod/module/mouthhole/on_uninstall() - if(!(former_flags & HEADCOVERSMOUTH)) + if(former_flags & HEADCOVERSMOUTH) mod.helmet.flags_cover |= HEADCOVERSMOUTH - if(!(former_visor_flags & HEADCOVERSMOUTH)) + if(former_visor_flags & HEADCOVERSMOUTH) mod.helmet.visor_flags_cover |= HEADCOVERSMOUTH ///EMP Shield - Protects the suit from EMPs. diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index e4968d92e3b..f5bbd6ec834 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -48,7 +48,7 @@ mod.wearer.emote("scream") playsound(mod.wearer, 'sound/effects/snap.ogg', 75, TRUE, frequency = 0.5) playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5) - mod.wearer.apply_damage(500, BRUTE, sharpness = SHARP_POINTY, wound_bonus = -50) //boggers, bogchamp, etc + mod.wearer.apply_damage(500, BRUTE, forced = TRUE, sharpness = SHARP_POINTY, def_zone = BODY_ZONE_CHEST) //boggers, bogchamp, etc mod.wearer.death() //just in case, for some reason, they're still alive flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS) diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm index 271e31ce416..f44661f66da 100644 --- a/code/modules/mod/modules/modules_science.dm +++ b/code/modules/mod/modules/modules_science.dm @@ -159,3 +159,160 @@ if(!attached_module.mod?.wearer) return wearer.set_output(attached_module.mod.wearer) + +///Anomaly Locked - Causes the module to not function without an anomaly. +/obj/item/mod/module/anomaly_locked + name = "MOD anomaly locked module" + desc = "A form of a module, locked behind an anomalous core to function." + incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + /// The core item the module runs off. + var/obj/item/assembly/signaler/anomaly/core + /// Accepted types of anomaly cores. + var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly) + /// If this one starts with a core in. + var/prebuilt = FALSE + +/obj/item/mod/module/anomaly_locked/Initialize(mapload) + . = ..() + if(!prebuilt || !length(accepted_anomalies)) + return + var/core_path = pick(accepted_anomalies) + core = new core_path(src) + update_icon_state() + +/obj/item/mod/module/anomaly_locked/Destroy() + QDEL_NULL(core) + return ..() + +/obj/item/mod/module/anomaly_locked/examine(mob/user) + . = ..() + if(!length(accepted_anomalies)) + return + if(core) + . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...") + else + var/list/core_list = list() + for(var/path in accepted_anomalies) + var/atom/core_path = path + core_list += initial(core_path.name) + . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.") + +/obj/item/mod/module/anomaly_locked/on_select() + if(!core) + balloon_alert(mod.wearer, "no core!") + return + return ..() + +/obj/item/mod/module/anomaly_locked/on_process(delta_time) + . = ..() + if(!core) + return FALSE + +/obj/item/mod/module/anomaly_locked/on_active_process(delta_time) + if(!core) + return FALSE + return TRUE + +/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params) + if(item.type in accepted_anomalies) + if(core) + balloon_alert(user, "core already in!") + return + if(!user.transferItemToLoc(item, src)) + return + core = item + balloon_alert(user, "core installed") + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + update_icon_state() + else + return ..() + +/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!core) + balloon_alert(user, "no core!") + return + balloon_alert(user, "removing core...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + balloon_alert(user, "core removed") + core.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(core) + core = null + update_icon_state() + +/obj/item/mod/module/anomaly_locked/update_icon_state() + icon_state = initial(icon_state) + (core ? "-core" : "") + return ..() + +///Anti-Gravity - Makes the user weightless. +/obj/item/mod/module/anomaly_locked/antigrav + name = "MOD anti-gravity module" + desc = "A module that uses a gravitational core to make the user completely weightless." + icon_state = "antigrav" + module_type = MODULE_TOGGLE + complexity = 3 + active_power_cost = DEFAULT_CELL_DRAIN * 0.7 + cooldown_time = 0.5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) + +/obj/item/mod/module/anomaly_locked/antigrav/on_activation() + . = ..() + if(!.) + return + if(mod.wearer.has_gravity()) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + mod.wearer.AddElement(/datum/element/forced_gravity, 0) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + playsound(src, 'sound/effects/gravhit.ogg', 50) + +/obj/item/mod/module/anomaly_locked/antigrav/on_deactivation() + . = ..() + if(!.) + return + mod.wearer.RemoveElement(/datum/element/forced_gravity, 0) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + if(mod.wearer.has_gravity()) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + playsound(src, 'sound/effects/gravhit.ogg', 50) + +/obj/item/mod/module/anomaly_locked/antigrav/prebuilt + prebuilt = TRUE + +///Teleporter - Lets the user teleport to a nearby location. +/obj/item/mod/module/anomaly_locked/teleporter + name = "MOD teleporter module" + desc = "A module that uses a bluespace core to let the user transport their particles elsewhere." + icon_state = "teleporter" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CELL_DRAIN * 5 + cooldown_time = 5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace) + /// Time it takes to teleport + var/teleport_time = 3 SECONDS + +/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target) + . = ..() + if(!.) + return + var/turf/open/target_turf = get_turf(target) + if(!istype(target_turf) || target_turf.is_blocked_turf_ignore_climbable() || !(target_turf in view(mod.wearer))) + balloon_alert(mod.wearer, "invalid target!") + return + balloon_alert(mod.wearer, "teleporting...") + var/matrix/user_matrix = matrix(mod.wearer.transform) + animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = user_matrix.Scale(4, 0.25), easing = EASE_OUT) + if(!do_after(mod.wearer, teleport_time, target = mod)) + balloon_alert(mod.wearer, "interrupted!") + animate(mod.wearer, teleport_time, color = null, transform = user_matrix.Scale(0.25, 4), easing = EASE_IN) + return + animate(mod.wearer, teleport_time*0.1, color = null, transform = user_matrix.Scale(0.25, 4), easing = EASE_IN) + if(!do_teleport(mod.wearer, target_turf, asoundin = 'sound/effects/phasein.ogg')) + return + drain_power(use_power_cost) + +/obj/item/mod/module/anomaly_locked/teleporter/prebuilt + prebuilt = TRUE diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm index f4f2a1fed41..86b7a704294 100644 --- a/code/modules/mod/modules/modules_service.dm +++ b/code/modules/mod/modules/modules_service.dm @@ -69,13 +69,13 @@ incompatible_modules = list(/obj/item/mod/module/waddle) /obj/item/mod/module/waddle/on_suit_activation() - mod.AddComponent(/datum/component/squeak, list('sound/effects/clownstep1.ogg'=1,'sound/effects/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please + mod.boots.AddComponent(/datum/component/squeak, list('sound/effects/clownstep1.ogg'=1,'sound/effects/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please mod.wearer.AddElement(/datum/element/waddling) if(is_clown_job(mod.wearer.mind?.assigned_role)) SEND_SIGNAL(mod.wearer, COMSIG_ADD_MOOD_EVENT, "clownshoes", /datum/mood_event/clownshoes) /obj/item/mod/module/waddle/on_suit_deactivation() - qdel(mod.GetComponent(/datum/component/squeak)) + qdel(mod.boots.GetComponent(/datum/component/squeak)) mod.wearer.RemoveElement(/datum/element/waddling) if(is_clown_job(mod.wearer.mind?.assigned_role)) SEND_SIGNAL(mod.wearer, COMSIG_CLEAR_MOOD_EVENT, "clownshoes") diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index de64a6ba1d5..b97be7ab5e2 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -32,6 +32,8 @@ use_power_cost = DEFAULT_CELL_DRAIN incompatible_modules = list(/obj/item/mod/module/clamp) cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_clamp" + overlay_state_active = "module_clamp_on" /// The max amount of crates you can carry. var/max_crates = 3 /// The crates stored in the module. diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm index 58bd179c732..99069f8d093 100644 --- a/code/modules/mod/modules/modules_visor.dm +++ b/code/modules/mod/modules/modules_visor.dm @@ -6,7 +6,7 @@ desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you." module_type = MODULE_TOGGLE complexity = 2 - active_power_cost = DEFAULT_CELL_DRAIN*0.3 + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/visor) cooldown_time = 0.5 SECONDS /// The HUD type given by the visor. diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 24391048c4b..0daea4a0bf9 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1340,3 +1340,17 @@ id = "mod_plasma" materials = list(/datum/material/plasma = 1000, /datum/material/glass = 1000) build_path = /obj/item/mod/module/plasma_stabilizer + +/datum/design/module/mod_antigrav + name = "MOD Module: Anti-Gravity" + id = "mod_antigrav" + materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 1000) + build_path = /obj/item/mod/module/anomaly_locked/antigrav + department_type = MODULE_SCIENCE + +/datum/design/module/mod_teleporter + name = "MOD Module: Teleporter" + id = "mod_teleporter" + materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/bluespace = 1000) + build_path = /obj/item/mod/module/anomaly_locked/teleporter + department_type = MODULE_SCIENCE diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 45055f92723..9c9b14f96f1 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1629,6 +1629,17 @@ ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) +/datum/techweb_node/mod_anomaly + id = "mod_anomaly" + display_name = "Anomalock Modular Suits" + description = "Modules for modular suits that require anomaly cores to function." + prereq_ids = list("mod_advanced", "anomaly_research") + design_ids = list( + "mod_antigrav", + "mod_teleporter", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000) + ////////////////////////mech technology//////////////////////// /datum/techweb_node/adv_mecha id = "adv_mecha" diff --git a/icons/mob/actions/actions_mod.dmi b/icons/mob/actions/actions_mod.dmi index 9c99abc6758..7f030ad53d4 100644 Binary files a/icons/mob/actions/actions_mod.dmi and b/icons/mob/actions/actions_mod.dmi differ diff --git a/icons/mob/mod.dmi b/icons/mob/mod.dmi index 51657f3d425..d47e420eccd 100644 Binary files a/icons/mob/mod.dmi and b/icons/mob/mod.dmi differ diff --git a/icons/obj/mod.dmi b/icons/obj/mod.dmi index 954b4ad1280..e34fe3094b9 100644 Binary files a/icons/obj/mod.dmi and b/icons/obj/mod.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 0f8e4dd608a..d5c854d8332 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -913,10 +913,10 @@ #include "code\datums\elements\climbable.dm" #include "code\datums\elements\connect_loc.dm" #include "code\datums\elements\crackable.dm" -#include "code\datums\elements\curse_announcement.dm" -#include "code\datums\elements\cursed.dm" #include "code\datums\elements\cult_eyes.dm" #include "code\datums\elements\cult_halo.dm" +#include "code\datums\elements\curse_announcement.dm" +#include "code\datums\elements\cursed.dm" #include "code\datums\elements\death_drops.dm" #include "code\datums\elements\deferred_aquarium_content.dm" #include "code\datums\elements\delete_on_drop.dm"