mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 18:11:16 +00:00
## About The Pull Request Slightly reworks the mining MODsuit to be more distinct from other mining gear and have its own designated role as an exploration and mining tool. - Base armor (when covered in ash) has been reduced to 50 from 60, being equal to that of an explorer suit with two goliath plates attached. However, entering the sphere mode will grant additional 20 armor, bumping it up to 70 (equal to that of a H.E.C.K. suit) - Integrated drill no longer mines instantly by default, instead having a delay of 0.25 seconds. However, when entering the sphere mode, the drill will overcharge and get back its instamine, as well as get halved power consumption. Currently, those two are mutually exclusive, and the drill cannot be used in the sphere mode. - Mining bomb cooldown has been reduced to 1s from 1.25s. They also now detonate much faster, and the detonation time matches their animation. The digging radius has been reduced back to 3x3 from 5x5, and their damage has been reduced to 28 from 48 to compensate increase in firing speed and reduced detonation delay making them much easier to use (functional DPS has been reduced from 36 to 28) - Rewrote ore bag a bit to try and make sure it doesn't break when depositing ores into the ORM. I only have faint suspicions of this being possibly being caused by ore getting deleted and leaving a null in the list, so removing it should hopefully? stop the bag from breaking. - The 0.25 slowdown is back, but it should be less of a problem considering that the sphere mode now is a much more viable traversal tool and not an utter joke aside from lava traversal. - The MODsuit now comes pre-equipped with a magnetic harness, which is now capable of stowing kinetic crushers in addition to guns. This should make using the sphere mode less of a pain in the ass, as you won't drop your weapon whenever you enter the sphere mode before you remember to put it in your suit storage slot. The delay on harnesses has also been reduced to 0.5 seconds, which should make them more comfortable to use, while still allowing someone to grab your gun if you're not careful. - The sphere mode can no longer traverse lava roundstart, instead requiring to be upgraded with two pieces of bileworm skin to get lava-resistant plating. This is meant to work together with #92877, being a part of ongoing effort to bring mining back in terms of speed and action level, reducing mining and exploration speeds in favor of higher ore spawns and more focus on gear and equipment progression. <img width="92" height="98" alt="image" src="https://github.com/user-attachments/assets/740ab28d-210d-4832-ba07-00dbd8680491" /> Additionally, both the mining drill and green raptor bumpmining has been nerfed (technically fixed, practically nerfed) by removing the diagonal movement... thing which allowed you to mine thrice as quickly and ignore the one-tick movement delay due to how diagonal movement works. https://github.com/user-attachments/assets/711e895f-e7e7-4cd9-b484-d7d11ff597af Its still fast and comfortable to use, just not absurdly fast. ## Why It's Good For The Game The mining MODsuit is in a very weird place both balance and progression wise. Its very easy to get if you ignore vents, it has good armor stats, it allows you to partly ignore being set on fire (or fully if you get the regulator module, but that requires more effort). I don't think that the buff was very needed, it was very strong as-is when used properly (with a yellow or green raptor mount) which not a lot of people seem to have realized. However, its still in a pretty pitiful state as its core feature (sphere mode) is nigh useless as the drill only works outside of it, and mines themselves are extremely clunky and uncomfortable to use. This leaves it only being useful for its cheap armor (without needing to kill goliaths), free GPS and ore bag that don't occupy your pockets, and ability to ignore environmental hazards. The solution I've decided to go with is reworking the MODsuit to be focused on mining and exploration rather than combat, being a good equipment piece for newer players and miners less interested in hunting megafauna. This carves the MODsuit its own niche rather than being weirdly slotted between base suits and contending with drake armor in terms of stats/effects. Roundstart lava crossing capabilities removal alongside bumpmining nerf is somewhat unrelated to the rest of the changes, it is a part of the exploration rework alongside #92877, which is intended to force miners to engage in combat more. Without the nerf, the suit is as fast as a yellow raptor, which lets it go through lavaland at absurd speeds when moving diagonally. (Yes, diagonal zig-zag movement ignoring the bump delay allows you to outspeed someone moving cardinally) More details about the project can be found in this [design doc](https://hackmd.io/@smart-kar/HkUINgBtke). The instamine ability of the sphere will likely get slightly nerfed in the future with the main batch of ore spread buffs and mining speed nerfs, but I've left it as powerful as a green raptor to not make the suit useless when compared to other options in the meantime. ## Changelog 🆑 add: Mining MODsuit has gained a magnetic harness for all of your crusher stowing needs. balance: Magnetic harnesses now take only 0.5 seconds to pick up your gun, and can pick up crushers. balance: Mining MODsuit has regained it small slowdown, and lost 10 melee armor. balance: Mining drill MODule is no longer instant (outside of the sphere mode of a mining MOD) balance: The mining sphere MODule now can break rocks when rolling into them, gives 20 melee and bomb armor when active, and has a shorter bomb cooldown, but bombs themselves have reduced damage and mining AOE. balance: Mining sphere MODule now requires an upgrade in form of two pieces of bileworm skin to be able to traverse lava, as opposed to being able to do so innately. fix: Fixed MODsuit ore bag sometimes breaking permanently when depositing ores into the ORM. /🆑
552 lines
19 KiB
Plaintext
552 lines
19 KiB
Plaintext
///MOD Module - A special device installed in a MODsuit allowing the suit to do new stuff.
|
|
/obj/item/mod/module
|
|
name = "MOD module"
|
|
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
|
icon_state = "module"
|
|
abstract_type = /obj/item/mod/module
|
|
/// If it can be removed
|
|
var/removable = TRUE
|
|
/// If it's passive, togglable, usable or active
|
|
var/module_type = MODULE_PASSIVE
|
|
/// Is the module active
|
|
var/active = FALSE
|
|
/// How much space it takes up in the MOD
|
|
var/complexity = 0
|
|
/// Power use when idle
|
|
var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0
|
|
/// Power use when active
|
|
var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0
|
|
/// Power use when used, we call it manually
|
|
var/use_energy_cost = DEFAULT_CHARGE_DRAIN * 0
|
|
/// ID used by their TGUI
|
|
var/tgui_id
|
|
/// Linked MODsuit
|
|
var/obj/item/mod/control/mod
|
|
/// If we're an active module, what item are we?
|
|
var/obj/item/device
|
|
/// Overlay given to the user when the module is inactive
|
|
var/overlay_state_inactive
|
|
/// Overlay given to the user when the module is active
|
|
var/overlay_state_active
|
|
/// Overlay given to the user when the module is used, lasts until cooldown finishes
|
|
var/overlay_state_use
|
|
/// Icon file for the overlay.
|
|
var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi'
|
|
/// Does the overlay use the control unit's colors?
|
|
var/use_mod_colors = FALSE
|
|
/// What modules are we incompatible with?
|
|
var/list/incompatible_modules = list()
|
|
/// Cooldown after use
|
|
var/cooldown_time = 0
|
|
/// The mouse button needed to use this module
|
|
var/used_signal
|
|
/// Are all parts needed active- have we ran on_part_activation
|
|
var/part_activated = FALSE
|
|
/// Do we need the parts to be extended to run process
|
|
var/part_process = TRUE
|
|
/// List of REF()s mobs we are pinned to, linked with their action buttons
|
|
var/list/pinned_to = list()
|
|
/// flags that let the module ability be used in odd circumstances
|
|
var/allow_flags = NONE
|
|
/// A list of slots required in the suit to work. Formatted like list(x|y, z, ...) where either x or y are required and z is required.
|
|
var/list/required_slots = list()
|
|
/// If TRUE worn overlay will be masked with the suit, preventing any bits from poking out of its controur
|
|
var/mask_worn_overlay = FALSE
|
|
/// Timer for the cooldown
|
|
COOLDOWN_DECLARE(cooldown_timer)
|
|
|
|
/obj/item/mod/module/Initialize(mapload)
|
|
. = ..()
|
|
if(module_type != MODULE_ACTIVE)
|
|
return
|
|
if(ispath(device))
|
|
device = new device(src)
|
|
ADD_TRAIT(device, TRAIT_NODROP, MOD_TRAIT)
|
|
RegisterSignal(device, COMSIG_QDELETING, PROC_REF(on_device_deletion))
|
|
RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
|
|
|
|
/obj/item/mod/module/Destroy()
|
|
mod?.uninstall(src)
|
|
if(device)
|
|
UnregisterSignal(device, COMSIG_QDELETING)
|
|
QDEL_NULL(device)
|
|
return ..()
|
|
|
|
/obj/item/mod/module/examine(mob/user)
|
|
. = ..()
|
|
if(length(required_slots))
|
|
var/list/slot_strings = list()
|
|
for(var/slot in required_slots)
|
|
var/list/slot_list = parse_slot_flags(slot)
|
|
slot_strings += (length(slot_list) == 1 ? "" : "one of ") + english_list(slot_list, and_text = " or ")
|
|
. += span_notice("Requires the MOD unit to have the following slots: [english_list(slot_strings)]")
|
|
if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
|
|
. += span_notice("Complexity level: [complexity]")
|
|
|
|
/// Looks through the MODsuit's parts to see if it has the parts required to support this module
|
|
/obj/item/mod/module/proc/has_required_parts(list/parts, need_active = FALSE)
|
|
if(!length(required_slots))
|
|
return TRUE
|
|
var/total_slot_flags = NONE
|
|
for(var/part_slot in parts)
|
|
if(need_active)
|
|
var/datum/mod_part/part_datum = parts[part_slot]
|
|
if(!part_datum.sealed)
|
|
continue
|
|
total_slot_flags |= text2num(part_slot)
|
|
var/list/needed_slots = required_slots.Copy()
|
|
for(var/needed_slot in needed_slots)
|
|
if(!(needed_slot & total_slot_flags))
|
|
break
|
|
needed_slots -= needed_slot
|
|
return !length(needed_slots)
|
|
|
|
/// Additional checks for whenever a module can be installed into a suit or not
|
|
/obj/item/mod/module/proc/can_install(obj/item/mod/control/mod)
|
|
return TRUE
|
|
|
|
/// Called when the module is selected from the TGUI, radial or the action button
|
|
/obj/item/mod/module/proc/on_select(mob/activator)
|
|
if(!mod.wearer && !(allow_flags & MODULE_ALLOW_UNWORN)) //No wearer and cannot be used unworn
|
|
balloon_alert(activator, "not equipped!")
|
|
return
|
|
if(((!mod.active || mod.activating) && !(allow_flags & (MODULE_ALLOW_INACTIVE | MODULE_ALLOW_UNWORN))) || module_type == MODULE_PASSIVE) // not active
|
|
balloon_alert(activator, "not active!")
|
|
return
|
|
if(!has_required_parts(mod.mod_parts, need_active = TRUE) && !(allow_flags & MODULE_ALLOW_UNWORN)) // Doesn't have parts
|
|
balloon_alert(activator, "required parts inactive!")
|
|
var/list/slot_strings = list()
|
|
for(var/slot in required_slots)
|
|
var/list/slot_list = parse_slot_flags(slot)
|
|
slot_strings += (length(slot_list) == 1 ? "" : "one of ") + english_list(slot_list, and_text = " or ")
|
|
to_chat(activator, span_warning("[src] requires these slots to be deployed: [english_list(slot_strings)]"))
|
|
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
|
return
|
|
if(module_type != MODULE_USABLE)
|
|
if(active)
|
|
deactivate(activator)
|
|
else
|
|
activate(activator)
|
|
else
|
|
used(activator)
|
|
SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src)
|
|
|
|
/// Apply a cooldown until this item can be used again
|
|
/obj/item/mod/module/proc/start_cooldown(applied_cooldown)
|
|
if (isnull(applied_cooldown))
|
|
applied_cooldown = cooldown_time
|
|
COOLDOWN_START(src, cooldown_timer, applied_cooldown)
|
|
SEND_SIGNAL(src, COMSIG_MODULE_COOLDOWN_STARTED, applied_cooldown)
|
|
|
|
/// Called when the module is activated
|
|
/obj/item/mod/module/proc/activate(mob/activator)
|
|
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
|
balloon_alert(activator, "on cooldown!")
|
|
return FALSE
|
|
if(!mod.active || mod.activating || !mod.get_charge())
|
|
balloon_alert(activator, "unpowered!")
|
|
return FALSE
|
|
if(!(allow_flags & MODULE_ALLOW_PHASEOUT) && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
|
|
//specifically a to_chat because the user is phased out.
|
|
to_chat(activator, span_warning("You cannot activate this right now."))
|
|
return FALSE
|
|
if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED, mod.wearer) & MOD_ABORT_USE)
|
|
return FALSE
|
|
if(module_type == MODULE_ACTIVE)
|
|
if(mod.selected_module && !mod.selected_module.deactivate(display_message = FALSE))
|
|
return FALSE
|
|
mod.selected_module = src
|
|
if(device)
|
|
if(mod.wearer.put_in_hands(device))
|
|
balloon_alert(activator, "[device] extended")
|
|
RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
|
|
RegisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey))
|
|
else
|
|
balloon_alert(activator, "can't extend [device]!")
|
|
mod.wearer.transferItemToLoc(device, src, force = TRUE)
|
|
return FALSE
|
|
else
|
|
var/used_button = mod.wearer.client?.prefs.read_preference(/datum/preference/choiced/mod_select) || MIDDLE_CLICK
|
|
update_signal(used_button)
|
|
balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use") // As of now, only wearers can "use" mods
|
|
active = TRUE
|
|
SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED)
|
|
SEND_SIGNAL(mod, COMSIG_MOD_MODULE_ACTIVATED, src)
|
|
on_activation(activator)
|
|
update_clothing_slots()
|
|
return TRUE
|
|
|
|
/// Called when the module is deactivated
|
|
/obj/item/mod/module/proc/deactivate(mob/activator, display_message = TRUE, deleting = FALSE)
|
|
active = FALSE
|
|
if(module_type == MODULE_ACTIVE)
|
|
mod.selected_module = null
|
|
if(display_message)
|
|
balloon_alert(mod.wearer, device ? "[device] retracted" : "[src] deactivated")
|
|
if(device)
|
|
mod.wearer.transferItemToLoc(device, src, force = TRUE)
|
|
UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED)
|
|
UnregisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN)
|
|
else
|
|
UnregisterSignal(mod.wearer, used_signal)
|
|
used_signal = null
|
|
SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED, mod.wearer)
|
|
SEND_SIGNAL(mod, COMSIG_MOD_MODULE_DEACTIVATED, src)
|
|
on_deactivation(activator, display_message = TRUE, deleting = FALSE)
|
|
update_clothing_slots()
|
|
return TRUE
|
|
|
|
/// Call to update all slots visually affected by this module
|
|
/obj/item/mod/module/proc/update_clothing_slots()
|
|
if(!mod.wearer)
|
|
return
|
|
|
|
var/updated_slots = mod.slot_flags
|
|
if (mask_worn_overlay)
|
|
for (var/obj/item/part as anything in mod.get_parts())
|
|
updated_slots |= part.slot_flags
|
|
else if (length(required_slots))
|
|
for (var/slot in required_slots)
|
|
updated_slots |= slot
|
|
mod.wearer.update_clothing(updated_slots)
|
|
|
|
/// Called when the module is used
|
|
/obj/item/mod/module/proc/used(mob/activator)
|
|
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
|
balloon_alert(activator, "on cooldown!")
|
|
return FALSE
|
|
if(!check_power(use_energy_cost))
|
|
balloon_alert(activator, "not enough charge!")
|
|
return FALSE
|
|
if(!(allow_flags & MODULE_ALLOW_PHASEOUT) && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
|
|
//specifically a to_chat because the user is phased out.
|
|
to_chat(activator, span_warning("You cannot activate this right now."))
|
|
return FALSE
|
|
if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED, mod.wearer) & MOD_ABORT_USE)
|
|
return FALSE
|
|
start_cooldown()
|
|
if(mod.wearer)
|
|
addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_clothing), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts
|
|
update_clothing_slots()
|
|
SEND_SIGNAL(src, COMSIG_MODULE_USED)
|
|
on_use(activator)
|
|
return TRUE
|
|
|
|
/// Called when an activated module without a device is used
|
|
/obj/item/mod/module/proc/on_select_use(atom/target)
|
|
if(!(allow_flags & MODULE_ALLOW_INCAPACITATED) && INCAPACITATED_IGNORING(mod.wearer, INCAPABLE_GRAB))
|
|
return FALSE
|
|
mod.wearer.face_atom(target)
|
|
if(!used())
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Called when an activated module without a device is active and the user alt/middle-clicks
|
|
/obj/item/mod/module/proc/on_special_click(mob/source, atom/target)
|
|
SIGNAL_HANDLER
|
|
on_select_use(target)
|
|
return COMSIG_MOB_CANCEL_CLICKON
|
|
|
|
/// Called on the MODsuit's process
|
|
/obj/item/mod/module/proc/on_process(seconds_per_tick)
|
|
if(part_process && !part_activated)
|
|
return FALSE
|
|
if(active)
|
|
if(!drain_power(active_power_cost * seconds_per_tick))
|
|
deactivate()
|
|
return FALSE
|
|
on_active_process(seconds_per_tick)
|
|
else
|
|
drain_power(idle_power_cost * seconds_per_tick)
|
|
return TRUE
|
|
|
|
/// Called from the module's activate()
|
|
/obj/item/mod/module/proc/on_activation(mob/activator)
|
|
return
|
|
|
|
/// Called from the module's deactivate()
|
|
/obj/item/mod/module/proc/on_deactivation(mob/activator, display_message = TRUE, deleting = FALSE)
|
|
return
|
|
|
|
/// Called from the module's used()
|
|
/obj/item/mod/module/proc/on_use(mob/activator)
|
|
return
|
|
|
|
/// Called on the MODsuit's process if it is an active module
|
|
/obj/item/mod/module/proc/on_active_process(seconds_per_tick)
|
|
return
|
|
|
|
/// Called from MODsuit's install() proc, so when the module is installed
|
|
/obj/item/mod/module/proc/on_install()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
|
|
if (mask_worn_overlay)
|
|
for (var/obj/item/part as anything in mod.get_parts(all = TRUE))
|
|
RegisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay))
|
|
return
|
|
|
|
if (!length(required_slots))
|
|
RegisterSignal(mod, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay))
|
|
return
|
|
|
|
var/obj/item/part = mod.get_part_from_slot(required_slots[1])
|
|
RegisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay))
|
|
|
|
/// Called from MODsuit's uninstall() proc, so when the module is uninstalled
|
|
/obj/item/mod/module/proc/on_uninstall(deleting = FALSE)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
|
|
if (mask_worn_overlay)
|
|
for (var/obj/item/part as anything in mod.get_parts(all = TRUE))
|
|
UnregisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS)
|
|
return
|
|
|
|
if (!length(required_slots))
|
|
UnregisterSignal(mod, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS)
|
|
return
|
|
|
|
var/obj/item/part = mod.get_part_from_slot(required_slots[1])
|
|
UnregisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS)
|
|
|
|
/// Called when the MODsuit is activated
|
|
/obj/item/mod/module/proc/on_part_activation()
|
|
return
|
|
|
|
/// Called when the MODsuit is deactivated
|
|
/obj/item/mod/module/proc/on_part_deactivation(deleting = FALSE)
|
|
return
|
|
|
|
/// Called when the MODsuit is equipped
|
|
/obj/item/mod/module/proc/on_equip()
|
|
return
|
|
|
|
/// Called when the MODsuit is unequipped
|
|
/obj/item/mod/module/proc/on_unequip()
|
|
return
|
|
|
|
/// Drains power from the suit charge
|
|
/obj/item/mod/module/proc/drain_power(amount)
|
|
if(!check_power(amount))
|
|
return FALSE
|
|
mod.subtract_charge(amount)
|
|
return TRUE
|
|
|
|
/// Checks if there is enough power in the suit
|
|
/obj/item/mod/module/proc/check_power(amount)
|
|
return mod.check_charge(amount)
|
|
|
|
/// Adds additional things to the MODsuit ui_data()
|
|
/obj/item/mod/module/proc/add_ui_data()
|
|
return list()
|
|
|
|
/// Creates a list of configuring options for this module, possible configs include number, bool, color, list, button.
|
|
/obj/item/mod/module/proc/get_configuration(mob/user)
|
|
return list()
|
|
|
|
/// Generates an element of the get_configuration list with a display name, type and value
|
|
/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values)
|
|
return list("display_name" = display_name, "type" = type, "value" = value, "values" = values)
|
|
|
|
/// Receives configure edits from the TGUI and edits the vars
|
|
/obj/item/mod/module/proc/configure_edit(key, value)
|
|
return
|
|
|
|
/// Called when the device moves to a different place on active modules
|
|
/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!active)
|
|
return
|
|
if(part.loc == src)
|
|
return
|
|
if(part.loc == mod.wearer)
|
|
return
|
|
if(part == device)
|
|
deactivate(display_message = FALSE)
|
|
|
|
/// Called when the device gets deleted on active modules
|
|
/obj/item/mod/module/proc/on_device_deletion(datum/source)
|
|
SIGNAL_HANDLER
|
|
|
|
if(source == device)
|
|
device.moveToNullspace()
|
|
device = null
|
|
qdel(src)
|
|
|
|
/// Adds the worn overlays to the suit.
|
|
/obj/item/mod/module/proc/add_module_overlay(obj/item/source, list/overlays, mutable_appearance/standing, mutable_appearance/draw_target, isinhands, icon_file)
|
|
SIGNAL_HANDLER
|
|
|
|
if (isinhands)
|
|
return
|
|
|
|
var/list/added_overlays = generate_worn_overlay(source, standing)
|
|
if (!added_overlays)
|
|
return
|
|
|
|
if (!mask_worn_overlay)
|
|
overlays += added_overlays
|
|
return
|
|
|
|
for (var/mutable_appearance/overlay as anything in added_overlays)
|
|
overlay.add_filter("mod_mask_overlay", 1, alpha_mask_filter(icon = icon(draw_target.icon, draw_target.icon_state)))
|
|
overlays += overlay
|
|
|
|
/// Generates an icon to be used for the suit's worn overlays
|
|
/obj/item/mod/module/proc/generate_worn_overlay(obj/item/source, mutable_appearance/standing)
|
|
if(!mask_worn_overlay)
|
|
if(!has_required_parts(mod.mod_parts, need_active = TRUE))
|
|
return
|
|
else
|
|
var/datum/mod_part/part_datum = mod.get_part_datum(source)
|
|
if (!part_datum?.sealed)
|
|
return
|
|
|
|
. = list()
|
|
var/used_overlay = get_current_overlay_state()
|
|
if (!used_overlay)
|
|
return
|
|
|
|
var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay, layer = standing.layer + 0.1)
|
|
if(use_mod_colors)
|
|
module_icon.color = mod.color
|
|
if (mod.cached_color_filter)
|
|
module_icon = filter_appearance_recursive(module_icon, mod.cached_color_filter)
|
|
|
|
. += module_icon
|
|
SEND_SIGNAL(src, COMSIG_MODULE_GENERATE_WORN_OVERLAY, ., standing)
|
|
|
|
/obj/item/mod/module/proc/get_current_overlay_state()
|
|
if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer))
|
|
return overlay_state_use
|
|
if(overlay_state_active && active)
|
|
return overlay_state_active
|
|
if(overlay_state_inactive)
|
|
return overlay_state_inactive
|
|
return null
|
|
|
|
/// Updates the signal used by active modules to be activated
|
|
/obj/item/mod/module/proc/update_signal(value)
|
|
switch(value)
|
|
if(MIDDLE_CLICK)
|
|
mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON
|
|
if(ALT_CLICK)
|
|
mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON
|
|
RegisterSignal(mod.wearer, mod.selected_module.used_signal, TYPE_PROC_REF(/obj/item/mod/module, on_special_click))
|
|
|
|
/// Pins the module to the user's action buttons
|
|
/obj/item/mod/module/proc/pin(mob/user)
|
|
if(module_type == MODULE_PASSIVE)
|
|
return
|
|
|
|
var/datum/action/item_action/mod/pinnable/module/existing_action = pinned_to[REF(user)]
|
|
if(existing_action)
|
|
mod.remove_item_action(existing_action)
|
|
return
|
|
|
|
var/datum/action/item_action/mod/pinnable/module/new_action = new(mod, user, src)
|
|
mod.add_item_action(new_action)
|
|
|
|
/// On drop key, concels a device item.
|
|
/obj/item/mod/module/proc/dropkey(mob/living/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if(user.get_active_held_item() != device)
|
|
return
|
|
deactivate()
|
|
return COMSIG_KB_ACTIVATED
|
|
|
|
///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()
|
|
/// 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
|
|
/// If the core is removable once socketed.
|
|
var/core_removable = TRUE
|
|
|
|
/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. [core_removable ? "You could remove it with a <b>screwdriver</b>..." : "Unfortunately, due to a design quirk, it's unremovable."]")
|
|
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.")
|
|
if(!core_removable)
|
|
. += span_notice("Due to some design quirk, once a core is inserted, it won't be removable.")
|
|
|
|
/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(seconds_per_tick)
|
|
. = ..()
|
|
if(!core)
|
|
return FALSE
|
|
|
|
/obj/item/mod/module/anomaly_locked/on_active_process(seconds_per_tick)
|
|
if(!core)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers)
|
|
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
|
|
if(!core_removable)
|
|
balloon_alert(user, "already has 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 ..()
|