mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-14 16:44:33 +01:00
Modsuits. Ready for review, sorry for the ammount of files changed 👉 👈 (#21147)
* biting off more than I can chew, but I feel like hurting myself * more shit * progress continues, moral stays sameish * bite of 2587 * mod_theme down, but probably broken * 385 errors, 16 warnings, zero tgui interactions * 292 19 * final strech to buggy mess, break time * it compiles. It almost fucking certainly doesnt work, but it compiles * fudhsciffubgsbgpgb actions not working as should for modules * fuck it we'll just open the pr as we work on it * icons work now * tgui stuff * more progress * JUDGEMENT * IT LIVES. TGUI LIVES. Special thanks to Sirryan and my brother. * jetpacks, gps, and go do what exo-suits yourself * almost ready * sorry to whoever has to review this * should work™️ * should be good now * pushing changes so I can do reviews next * Apply suggestions from code review Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * lore™️ Adds illegals level to modsuit parts from syndicate. * early returns * and these illegals * no uplink reference idiot * mod inhands are a good idea * this is needed for inhands nerd * Bomb detector working, modsutis cover ears. skin things * overslotting removal, cyborg charging, ert loadout for gamma * duplicated signal * modsuit themes, removes broken mister / bad base type module, 2 cores in robotics. * tether module, orb module, holster stuff * built in no slip, cheaper 87 * removes modules already installed, engineer ert get jetpack, equip into modsuit storage * comments spaces and duplicate flags * Some adjustments and thoughts on armor, heads get guns because they are cool like that / looting command members armor * Apply suggestions from code review Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Compile TGUI, slowdown adjustment, Modsuit to MODsuit * sonar, do after, more equipment, acid handling * overlay bugs / ERT / DS suits autodeploy * middle click action buttons * traders, sec modsuit energy nerf * surplus weighting * Kinesis module. * Pathfinder module, kinesis balance, blueprint for pathfinder and kinesis * more power / unused module removed * name, ds fix, pre equipped to cc suit * depot / grammer * ok should be good™️ * overslot, dupe gun, missing suit storage * stealth nerf, new kit * gun stuff / ds theme * bio armor is gone * let me push changes please thank you * makes shocks work right * copies over some changes TG made to module prices after porting * Merge remote-tracking branch 'upstream/master' into Because-I-hate-myself-and-someone-has-to-try-it- * sirryans changes Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/telesci/gps.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/telesci/gps.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Control define, ERT / DS have gloves / boots again. * Apply suggestions from code review Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * More changes * god I hate tgui. Why do all the bundles live underground, instead of in my basement? * she updates my master till I testmerge * modsuits can recharge again * fixes a lot of shit * Ready for TM probably * sure fuck it whatever that makes sense I guess * the pinpointer works better™️ * Another storage module bug fix * hair and medical stuff * fuck fuck fuck * fixes sonar * Bag exploit fixes / plasma runtimes / ear nerf * dna lock improvement / cryostorage * whoops * Ball form second cooldown wasn't helping, no more infinite cores * she springs my lock till I ***SNAP*** * plasmaman no longer die * fixes modsuit emp issues * atmos no longer fucking dies * Fixes, module printing, and a restriction on newly constructed modsuits * Inventory code my hated * WOOO PREFERENCE CODE WOOOOO * icons my hated * fix for mods / spaceproof shit * holy shit whhhyyyyy * backpack first * Update mod_theme.dm * prevent infinite gun arms / infinite modsuit cores / syndicate modules * fixes tm error * scream loudly if species sprites break. They shouldn't, but, you know. * deconflicted with luck * converts to refit for species * fix pathfinder * fixes modsuits being funky power wise * lockboxes :gatto: , as well as DN-MP shield lock, and slight nerf to atmos modsuit * nerfs the atmos modsuit * magboots for ds / CC modsuit * downside of autosave: typing in wrong window saves bad stuff * Gamma ERT have internal cameras again. * spellcheck when * ah fuck * yes delete the null that works * whoops * fixes error / description * un vulps your cannon * fixes tails disapeering on base modsuits * space to underscore * tgui fix / tajaran * springlock buff, cryopod fix * u helps * WHO PUTS A GUN IN ITEMS * tgui moment * mining modsuit nerf / hidden holster module * PAIN * UNATHI TIDE STATION WIDE * I love special code * slightly dense * shark jumpscare * fixes ghost item, fixes springlock being bad * YIPPEEEE IT WORKED * she decons my flict * pain * ebbas days are numbered :^) * hidden * r-r-r-ebuild * fixes more bugs * qwerty, you are dense * fixes gps * fix mulebot crate * level fix * adds back icon * moving storage, fix bug, modules activate / deactivate message * yeah * yeah * yeah™️ * steels requests * windoors no longer make pathfinder module explode * chat spam * oil slicks + modsuits * whoops * bag check * ofc * Update code/modules/mod/mod_control.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/datums/uplink_items/uplink_nuclear.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/modules/mod/mod_control.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/modules/mod/modules/module_pathfinder.dm Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> --------- Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,431 @@
|
||||
///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"
|
||||
/// 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_power_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
|
||||
///Does the mod overide the colour in some way?
|
||||
var/mod_color_overide
|
||||
/// 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
|
||||
/// List of UID()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
|
||||
/// Timer for the cooldown
|
||||
COOLDOWN_DECLARE(cooldown_timer) //sohtgdoiuduhnfipguhndshnfigdnghd
|
||||
///The UID of the module. Don't ask.
|
||||
var/module_UID = null
|
||||
sprite_sheets = list(
|
||||
"Grey" = 'icons/mob/clothing/modsuit/species/grey_mod_modules.dmi',
|
||||
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/modules_vulp.dmi',
|
||||
"Tajaran" = 'icons/mob/clothing/modsuit/species/modules_taj.dmi',
|
||||
"Unathi" = 'icons/mob/clothing/modsuit/species/modules_unathi.dmi'
|
||||
)
|
||||
|
||||
/obj/item/mod/module/Initialize(mapload)
|
||||
. = ..()
|
||||
module_UID = UID(src)
|
||||
if(module_type != MODULE_ACTIVE)
|
||||
return
|
||||
if(ispath(device))
|
||||
device = new device(src)
|
||||
device.flags |= NODROP
|
||||
device.resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
device.slot_flags = null
|
||||
device.w_class = WEIGHT_CLASS_HUGE
|
||||
device.materials = null
|
||||
RegisterSignal(device, COMSIG_PARENT_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_PARENT_QDELETING)
|
||||
QDEL_NULL(device)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/examine(mob/user)
|
||||
. = ..()
|
||||
. += "Complexity level: [complexity]"
|
||||
|
||||
|
||||
/// 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) && !(allow_flags & MODULE_ALLOW_INACTIVE)) || module_type == MODULE_PASSIVE)
|
||||
if(mod.wearer)
|
||||
to_chat(mod.wearer, "<span class='warning'>Module is not active!</span>")
|
||||
return
|
||||
if(module_type != MODULE_USABLE)
|
||||
if(active)
|
||||
on_deactivation()
|
||||
else
|
||||
on_activation()
|
||||
else
|
||||
on_use()
|
||||
SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src)
|
||||
|
||||
/// Called when the module is activated
|
||||
/obj/item/mod/module/proc/on_activation()
|
||||
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
||||
to_chat(mod.wearer, "<span class='warning'>Module is on cooldown!</span>")
|
||||
return FALSE
|
||||
if(!mod.active || mod.activating || !mod.get_charge())
|
||||
to_chat(mod.wearer, "<span class='warning'>Module is unpowered!</span>")
|
||||
return FALSE
|
||||
if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
|
||||
return FALSE
|
||||
if(module_type == MODULE_ACTIVE)
|
||||
if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE))
|
||||
return FALSE
|
||||
mod.selected_module = src
|
||||
if(device)
|
||||
if(mod.wearer.put_in_hands(device))
|
||||
to_chat(mod.wearer, "<span class='notice'>[device] extended.</span>")
|
||||
RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
|
||||
RegisterSignal(mod.wearer, COMSIG_MOB_WILLINGLY_DROP, PROC_REF(dropkey))
|
||||
else
|
||||
to_chat(mod.wearer, "<span class='warning'>You can not extend the [device]!</span>")
|
||||
mod.wearer.drop_item()
|
||||
return FALSE
|
||||
else
|
||||
var/used_button = "Middle Click"
|
||||
if(!mod.wearer || !(mod.wearer.client.prefs.toggles2 & PREFTOGGLE_2_MOD_ACTIVATION_METHOD))
|
||||
used_button = "Alt Click"
|
||||
update_signal(used_button)
|
||||
to_chat(mod.wearer, "<span class='notice'>[src] activated, [used_button] to use.</span>")
|
||||
else
|
||||
COOLDOWN_START(src, cooldown_timer, cooldown_time) //We don't want to put active modules on cooldown when selected
|
||||
to_chat(mod.wearer, "<span class='notice'>[src] activated.</span>")
|
||||
active = TRUE
|
||||
mod.update_mod_overlays()
|
||||
//mod.wearer.update_clothing(mod.slot_flags)
|
||||
SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED)
|
||||
return TRUE
|
||||
|
||||
/// Called when the module is deactivated
|
||||
/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
active = FALSE
|
||||
if(module_type == MODULE_ACTIVE)
|
||||
mod.selected_module = null
|
||||
if(display_message && device)
|
||||
to_chat(mod.wearer, "<span class='notice'>[device] retracted.</span>")
|
||||
else if(display_message)
|
||||
to_chat(mod.wearer, "<span class='notice'>[src] deactivated.</span>")
|
||||
|
||||
if(device)
|
||||
mod.wearer.unEquip(device, 1)
|
||||
device.forceMove(src)
|
||||
UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED)
|
||||
UnregisterSignal(mod.wearer, COMSIG_MOB_WILLINGLY_DROP)
|
||||
else
|
||||
UnregisterSignal(mod.wearer, used_signal)
|
||||
used_signal = null
|
||||
else if(display_message)
|
||||
to_chat(mod.wearer, "<span class='notice'>[src] deactivated.</span>")
|
||||
//mod.wearer.update_clothing(mod.slot_flags)
|
||||
SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED)
|
||||
mod.update_mod_overlays()
|
||||
return TRUE
|
||||
|
||||
/// Called when the module is used
|
||||
/obj/item/mod/module/proc/on_use()
|
||||
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
||||
to_chat(mod.wearer, "<span class='warning'>Module is on cooldown!</span>")
|
||||
return FALSE
|
||||
if(!check_power(use_power_cost))
|
||||
to_chat(mod.wearer, "<span class='warning'>Module costs too much power to use!</span>")
|
||||
return FALSE
|
||||
if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
|
||||
return FALSE
|
||||
COOLDOWN_START(src, cooldown_timer, cooldown_time)
|
||||
//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
|
||||
//mod.wearer.update_clothing(mod.slot_flags)
|
||||
SEND_SIGNAL(src, COMSIG_MODULE_USED)
|
||||
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) && mod.wearer.incapacitated())
|
||||
return FALSE
|
||||
mod.wearer.face_atom(target)
|
||||
if(!on_use())
|
||||
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()
|
||||
if(active)
|
||||
if(!drain_power(active_power_cost))
|
||||
on_deactivation()
|
||||
return FALSE
|
||||
on_active_process()
|
||||
else
|
||||
drain_power(idle_power_cost)
|
||||
return TRUE
|
||||
|
||||
/// Called on the MODsuit's process if it is an active module
|
||||
/obj/item/mod/module/proc/on_active_process()
|
||||
return
|
||||
|
||||
/// Called from MODsuit's install() proc, so when the module is installed.
|
||||
/obj/item/mod/module/proc/on_install()
|
||||
return
|
||||
|
||||
/// Called from MODsuit's uninstall() proc, so when the module is uninstalled.
|
||||
/obj/item/mod/module/proc/on_uninstall(deleting = FALSE)
|
||||
mod.update_mod_overlays()
|
||||
return
|
||||
|
||||
/// Called when the MODsuit is activated
|
||||
/obj/item/mod/module/proc/on_suit_activation()
|
||||
mod.update_mod_overlays()
|
||||
return
|
||||
|
||||
/// Called when the MODsuit is deactivated
|
||||
/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE)
|
||||
mod.update_mod_overlays()
|
||||
return
|
||||
|
||||
/// Called when the MODsuit is equipped
|
||||
/obj/item/mod/module/proc/on_equip()
|
||||
mod.update_mod_overlays()
|
||||
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)
|
||||
mod.update_charge_alert()
|
||||
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
|
||||
/obj/item/mod/module/proc/get_configuration()
|
||||
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)
|
||||
on_deactivation(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 = null
|
||||
qdel(src)
|
||||
|
||||
/// Adds the worn overlays to the suit.
|
||||
/obj/item/mod/module/proc/add_module_overlay(mob/living/user)
|
||||
user.add_overlay(generate_worn_overlay(user))
|
||||
|
||||
/// Generates an icon to be used for the suit's worn overlays
|
||||
/obj/item/mod/module/proc/generate_worn_overlay(mob/living/carbon/human/user)
|
||||
. = list()
|
||||
if(!mod.active)
|
||||
return
|
||||
var/used_overlay
|
||||
if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer))
|
||||
used_overlay = overlay_state_use
|
||||
else if(overlay_state_active && active)
|
||||
used_overlay = overlay_state_active
|
||||
else if(overlay_state_inactive)
|
||||
used_overlay = overlay_state_inactive
|
||||
else
|
||||
return
|
||||
var/image/final_overlay
|
||||
if(sprite_sheets && sprite_sheets[user.dna.species.name])
|
||||
final_overlay = image(icon = sprite_sheets[user.dna.species.name], icon_state = used_overlay, layer = EFFECTS_LAYER)
|
||||
else
|
||||
final_overlay = image(icon = overlay_icon_file, icon_state = used_overlay, layer = EFFECTS_LAYER)
|
||||
if(mod_color_overide)
|
||||
final_overlay.color = mod_color_overide
|
||||
. += final_overlay
|
||||
mod.mod_overlays += final_overlay
|
||||
|
||||
/// 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
|
||||
if(length(pinned_to))
|
||||
for(var/datum/action/item_action/mod/pinned_module/M in user.actions)
|
||||
if(M.module == src)
|
||||
M.Remove(user)
|
||||
pinned_to = list()
|
||||
return
|
||||
var/datum/action/item_action/mod/pinned_module/new_action = new(Target = mod, custom_icon = src.icon, custom_icon_state = src.icon_state, linked_module = src, user = user)
|
||||
to_chat(user, "[new_action] is now pinned to the UI!")
|
||||
|
||||
|
||||
/// On drop key, concels a device item.
|
||||
/obj/item/mod/module/proc/dropkey(mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(user.get_active_hand() != device)
|
||||
return
|
||||
on_deactivation()
|
||||
return
|
||||
|
||||
///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)
|
||||
. += "There is a [core.name] installed in it. You could remove it with a <b>screwdriver</b>..."
|
||||
else
|
||||
var/list/core_list = list()
|
||||
for(var/path in accepted_anomalies)
|
||||
var/atom/core_path = path
|
||||
core_list += initial(core_path.name)
|
||||
. +="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)
|
||||
to_chat(mod.wearer, "<span class='warning'>ERROR. NO CORE INSTALLED!</span>")
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/on_process()
|
||||
. = ..()
|
||||
if(!core)
|
||||
return FALSE
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/on_active_process()
|
||||
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)
|
||||
to_chat(user, "<span class='warning'>A core is already installed!</span>")
|
||||
return
|
||||
if(!user.drop_item())
|
||||
return
|
||||
core = item
|
||||
to_chat(user, "<span class='notice'>You install [item].</span>")
|
||||
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
|
||||
update_icon_state()
|
||||
core.forceMove(src)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
. = ..()
|
||||
if(!core)
|
||||
to_chat(user, "<span class='warning'>A core is not installed!</span>")
|
||||
return
|
||||
if(!do_after(user, 3 SECONDS, target = src))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove [core].</span>")
|
||||
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 ..()
|
||||
@@ -0,0 +1,266 @@
|
||||
///Kinesis - Gives you the ability to move and launch objects.
|
||||
/obj/item/mod/module/anomaly_locked/kinesis
|
||||
name = "MOD kinesis module"
|
||||
desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \
|
||||
despite the suits it used to be mounted on still seeing some circulation. \
|
||||
This piece of technology allows the user to generate precise anti-gravity fields, \
|
||||
letting them move objects as small as a titanium rod to as large as industrial machinery. \
|
||||
It does seem to work on living creatures, but not well."
|
||||
icon_state = "kinesis"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 3
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 3
|
||||
incompatible_modules = list(/obj/item/mod/module/anomaly_locked/kinesis)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
overlay_state_inactive = "module_kinesis"
|
||||
overlay_state_active = "module_kinesis_on"
|
||||
accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav)
|
||||
/// Range of the kinesis grab.
|
||||
var/grab_range = 5
|
||||
/// Time between us hitting objects with kinesis.
|
||||
var/hit_cooldown_time = 1 SECONDS
|
||||
/// Stat required for us to grab a mob.
|
||||
var/stat_required = CONSCIOUS //Honestly. It's grav core locked. We'll try it, but I am going to need you to stun the mod. No fucking holding a poor terror prince in the air
|
||||
/// Is incapitated required for us to grab a mob?
|
||||
var/incapacitated_required = TRUE
|
||||
/// How long we stun a mob for.
|
||||
var/mob_stun_time = 0
|
||||
/// Atom we grabbed with kinesis.
|
||||
var/atom/movable/grabbed_atom
|
||||
/// Overlay we add to each grabbed atom.
|
||||
var/image/kinesis_icon
|
||||
/// Our mouse movement catcher.
|
||||
var/obj/screen/fullscreen/cursor_catcher/kinesis/kinesis_catcher
|
||||
/// The sounds playing while we grabbed an object.
|
||||
var/datum/looping_sound/kinesis/soundloop
|
||||
///The pixel_X of whatever we were grabbing before hand.
|
||||
var/pre_pixel_x
|
||||
///The pixel_y of whatever we were grabbing before hand.
|
||||
var/pre_pixel_y
|
||||
/// The special snowflake effect we need to get beams to work
|
||||
var/obj/effect/abstract/kinesis/beam = null
|
||||
/// The cooldown between us hitting objects with kinesis.
|
||||
COOLDOWN_DECLARE(hit_cooldown)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/Initialize(mapload)
|
||||
. = ..()
|
||||
soundloop = new(src)
|
||||
kinesis_icon = image(icon = 'icons/effects/effects.dmi', icon_state = "kinesis", layer = EFFECTS_LAYER)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/Destroy()
|
||||
QDEL_NULL(soundloop)
|
||||
QDEL_NULL(beam)
|
||||
QDEL_NULL(kinesis_catcher)
|
||||
QDEL_NULL(kinesis_icon)
|
||||
grabbed_atom = null
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!mod.wearer.client)
|
||||
return
|
||||
if(grabbed_atom)
|
||||
launch()
|
||||
clear_grab(playsound = FALSE)
|
||||
return
|
||||
if(!range_check(target))
|
||||
to_chat(mod.wearer, "<span class='warning'>[target] is too far away!</span>")
|
||||
return
|
||||
if(!can_grab(target))
|
||||
to_chat(mod.wearer, "<span class='warning'>[target] can not be grabbed!</span>")
|
||||
return
|
||||
drain_power(use_power_cost)
|
||||
grabbed_atom = target
|
||||
if(isliving(grabbed_atom))
|
||||
var/mob/living/grabbed_mob = grabbed_atom
|
||||
grabbed_mob.Stun(mob_stun_time)
|
||||
playsound(grabbed_atom, 'sound/weapons/contractorbatonhit.ogg', 75, TRUE)
|
||||
beam = new /obj/effect/abstract/kinesis(get_turf(mod.wearer))
|
||||
kinesis_icon.layer = grabbed_atom.layer - 0.1
|
||||
grabbed_atom.add_overlay(kinesis_icon)
|
||||
pre_pixel_x = grabbed_atom.pixel_x
|
||||
pre_pixel_y = grabbed_atom.pixel_y
|
||||
beam.chain = beam.Beam(grabbed_atom, icon_state = "kinesis", icon='icons/effects/beam.dmi', time = 100 SECONDS, maxdistance = 15, beam_type = /obj/effect/ebeam, beam_sleep_time = 3)
|
||||
kinesis_catcher = mod.wearer.overlay_fullscreen("kinesis", /obj/screen/fullscreen/cursor_catcher/kinesis, 0)
|
||||
kinesis_catcher.assign_to_mob(mod.wearer)
|
||||
soundloop.start()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
clear_grab(playsound = !deleting)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/process()
|
||||
if(!mod.wearer.client || mod.wearer.incapacitated(ignore_grab = TRUE))
|
||||
clear_grab()
|
||||
return
|
||||
if(!range_check(grabbed_atom))
|
||||
to_chat(mod.wearer, "<span class='warning'>[grabbed_atom] is too far away!</span>")
|
||||
clear_grab()
|
||||
return
|
||||
beam.forceMove(get_turf(mod.wearer))
|
||||
drain_power(use_power_cost / 10)
|
||||
if(kinesis_catcher.mouse_params)
|
||||
kinesis_catcher.calculate_params()
|
||||
if(!kinesis_catcher.given_turf)
|
||||
return
|
||||
mod.wearer.setDir(get_dir(mod.wearer, grabbed_atom))
|
||||
if(grabbed_atom.loc == kinesis_catcher.given_turf)
|
||||
if(grabbed_atom.pixel_x == kinesis_catcher.given_x - world.icon_size/2 && grabbed_atom.pixel_y == kinesis_catcher.given_y - world.icon_size/2)
|
||||
return //spare us redrawing if we are standing still
|
||||
animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = pre_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
|
||||
beam.chain.Reset()
|
||||
beam.chain.Draw()
|
||||
return
|
||||
animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = pre_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
|
||||
var/turf/next_turf = get_step_towards(grabbed_atom, kinesis_catcher.given_turf)
|
||||
if(grabbed_atom.Move(next_turf, get_dir(grabbed_atom, next_turf), 8))
|
||||
if(isitem(grabbed_atom) && (mod.wearer in next_turf))
|
||||
var/obj/item/grabbed_item = grabbed_atom
|
||||
clear_grab()
|
||||
grabbed_item.pickup(mod.wearer)
|
||||
mod.wearer.put_in_hands(grabbed_item)
|
||||
return
|
||||
var/pixel_x_change = 0
|
||||
var/pixel_y_change = 0
|
||||
var/direction = get_dir(grabbed_atom, next_turf)
|
||||
if(direction & NORTH)
|
||||
pixel_y_change = world.icon_size / 2
|
||||
else if(direction & SOUTH)
|
||||
pixel_y_change = -world.icon_size / 2
|
||||
if(direction & EAST)
|
||||
pixel_x_change = world.icon_size / 2
|
||||
else if(direction & WEST)
|
||||
pixel_x_change = -world.icon_size / 2
|
||||
animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + pixel_x_change, pixel_y = pre_pixel_y + pixel_y_change) //Not as smooth as I would like, will look into this in the future
|
||||
beam.chain.Reset()
|
||||
beam.chain.Draw()
|
||||
if(!isitem(grabbed_atom) || !COOLDOWN_FINISHED(src, hit_cooldown))
|
||||
return
|
||||
var/atom/hitting_atom
|
||||
if(next_turf.density)
|
||||
hitting_atom = next_turf
|
||||
for(var/atom/movable/movable_content as anything in next_turf.contents)
|
||||
if(ismob(movable_content))
|
||||
continue
|
||||
if(movable_content.density)
|
||||
hitting_atom = movable_content
|
||||
break
|
||||
var/obj/item/grabbed_item = grabbed_atom
|
||||
grabbed_item.melee_attack_chain(mod.wearer, hitting_atom)
|
||||
COOLDOWN_START(src, hit_cooldown, hit_cooldown_time)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/proc/can_grab(atom/target)
|
||||
if(mod.wearer == target)
|
||||
return FALSE
|
||||
if(!ismovable(target))
|
||||
return FALSE
|
||||
if(iseffect(target))
|
||||
return FALSE
|
||||
var/atom/movable/movable_target = target
|
||||
if(movable_target.anchored)
|
||||
return FALSE
|
||||
if(movable_target.throwing)
|
||||
return FALSE
|
||||
if(movable_target.move_resist >= MOVE_FORCE_OVERPOWERING)
|
||||
return FALSE
|
||||
if(ismob(movable_target))
|
||||
if(!isliving(movable_target))
|
||||
return FALSE
|
||||
var/mob/living/living_target = movable_target
|
||||
if(living_target.stat < stat_required)
|
||||
return FALSE
|
||||
if(!living_target.incapacitated() && incapacitated_required)
|
||||
return FALSE
|
||||
else if(isitem(movable_target))
|
||||
var/obj/item/item_target = movable_target
|
||||
if(item_target.w_class >= WEIGHT_CLASS_GIGANTIC)
|
||||
return FALSE
|
||||
if(item_target.flags & ABSTRACT)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/proc/clear_grab(playsound = TRUE)
|
||||
if(!grabbed_atom)
|
||||
return
|
||||
if(playsound)
|
||||
playsound(grabbed_atom, 'sound/effects/empulse.ogg', 75, TRUE)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
kinesis_catcher = null
|
||||
mod.wearer.clear_fullscreen("kinesis")
|
||||
grabbed_atom.cut_overlay(kinesis_icon)
|
||||
QDEL_NULL(beam)
|
||||
if(!isitem(grabbed_atom))
|
||||
animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x, pixel_y = pre_pixel_y)
|
||||
grabbed_atom = null
|
||||
soundloop.stop()
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/proc/range_check(atom/target)
|
||||
if(!isturf(mod.wearer.loc))
|
||||
return FALSE
|
||||
if(ismovable(target) && !isturf(target.loc))
|
||||
return FALSE
|
||||
if(!can_see(mod.wearer, target, grab_range))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/proc/launch()
|
||||
playsound(grabbed_atom, 'sound/magic/repulse.ogg', 100, TRUE)
|
||||
RegisterSignal(grabbed_atom, COMSIG_MOVABLE_IMPACT, PROC_REF(launch_impact))
|
||||
var/turf/target_turf = get_turf_in_angle(get_angle(mod.wearer, grabbed_atom), get_turf(src), 10)
|
||||
grabbed_atom.throw_at(target_turf, range = grab_range, speed = grabbed_atom.density ? 3 : 4, thrower = mod.wearer, spin = isitem(grabbed_atom))
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/proc/launch_impact(atom/movable/source, atom/hit_atom, datum/thrownthing/thrownthing)
|
||||
UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
|
||||
if(!isobj(source))
|
||||
return
|
||||
var/obj/S = source
|
||||
var/damage_self = TRUE
|
||||
var/damage = 8
|
||||
if(S.density)
|
||||
damage_self = FALSE
|
||||
damage = 15
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/living_atom = hit_atom
|
||||
living_atom.apply_damage(damage, BRUTE)
|
||||
else if(isobj(hit_atom))
|
||||
var/obj/O = hit_atom
|
||||
O.take_damage(damage, BRUTE, MELEE)
|
||||
if(damage_self)
|
||||
S.take_damage(S.max_integrity / 5, BRUTE, MELEE)
|
||||
|
||||
/obj/effect/abstract/kinesis
|
||||
var/datum/beam/chain
|
||||
|
||||
/obj/effect/abstract/kinesis/Destroy()
|
||||
qdel(chain)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/prebuilt
|
||||
prebuilt = TRUE
|
||||
removable = FALSE // No switching it into another suit / no free anomaly core
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype
|
||||
name = "MOD prototype kinesis module"
|
||||
complexity = 0
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
|
||||
|
||||
/obj/screen/fullscreen/cursor_catcher/kinesis
|
||||
icon_state = "kinesis"
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/kinesis/plus
|
||||
name = "MOD kinesis+ module"
|
||||
desc = "A modular plug-in to the forearm, this module was recently redeveloped in secret. \
|
||||
The bane of all ne'er-do-wells, the kinesis+ module is a powerful tool that allows the user \
|
||||
to manipulate the world around them. Like it's older counterpart, it's capable of manipulating \
|
||||
structures, machinery, vehicles, and, thanks to the fruitful efforts of it's creators - living \
|
||||
beings. They can, however, still struggle after an initial burst of inertia."
|
||||
complexity = 0
|
||||
prebuilt = TRUE
|
||||
stat_required = CONSCIOUS //Still conscious here so we don't forget about it if the above is changed
|
||||
incapacitated_required = FALSE
|
||||
mob_stun_time = 10 SECONDS
|
||||
@@ -0,0 +1,238 @@
|
||||
///Pathfinder - Can fly the suit from a long distance to an implant installed in someone.
|
||||
/obj/item/mod/module/pathfinder
|
||||
name = "MOD pathfinder module"
|
||||
desc = "This module, brought to you by Paizo Productions, has two components. \
|
||||
The first component is a series of thrusters and a computerized location subroutine installed into the \
|
||||
very control unit of the suit, allowing it flight at highway speeds using the suit's access locks \
|
||||
to navigate through the station, and to be able to locate the second part of the system; \
|
||||
a pathfinding implant installed into the base of the user's spine, \
|
||||
broadcasting their location to the suit and allowing them to recall it to their person at any time. \
|
||||
The implant is stored in the module and needs to be injected in a human to function. \
|
||||
Paizo Productions swears up and down there's airbrakes."
|
||||
icon_state = "pathfinder"
|
||||
complexity = 2
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 200
|
||||
incompatible_modules = list(/obj/item/mod/module/pathfinder)
|
||||
/// The pathfinding implant.
|
||||
var/obj/item/implant/mod/implant
|
||||
|
||||
/obj/item/mod/module/pathfinder/Initialize(mapload)
|
||||
. = ..()
|
||||
implant = new(src)
|
||||
|
||||
/obj/item/mod/module/pathfinder/Destroy()
|
||||
implant = null
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/pathfinder/examine(mob/user)
|
||||
. = ..()
|
||||
if(implant)
|
||||
. += "<span class='notice'>Use it on a human to implant them.</span>"
|
||||
else
|
||||
. += "<span class='warning'>The implant is missing.</span>"
|
||||
|
||||
/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params)
|
||||
if(!ishuman(target) || !implant)
|
||||
return
|
||||
if(!do_after(user, 1.5 SECONDS, target = target))
|
||||
return
|
||||
if(!implant.implant(target, user))
|
||||
to_chat(user, "<span class='warning'>Unable to implant [target]!</span>")
|
||||
return
|
||||
if(target == user)
|
||||
to_chat(user, "<span class='notice>'You implant yourself with [implant].</span>")
|
||||
else
|
||||
target.visible_message("<span class='notice'>[user] implants [target].</span>", "<span class='notice'>[user] implants you with [implant].</span>")
|
||||
playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
|
||||
icon_state = "pathfinder_empty"
|
||||
implant = null
|
||||
|
||||
/obj/item/mod/module/pathfinder/proc/attach(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(human_user.get_item_by_slot(slot_back) && !human_user.unEquip(human_user.get_item_by_slot(slot_back)))
|
||||
return
|
||||
if(!human_user.equip_to_slot_if_possible(mod, slot_back, disable_warning = TRUE))
|
||||
return
|
||||
mod.quick_deploy(user)
|
||||
human_user.update_action_buttons(TRUE)
|
||||
playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/implant/mod
|
||||
name = "MOD pathfinder implant"
|
||||
desc = "Lets you recall a MODsuit to you at any time."
|
||||
implant_data = /datum/implant_fluff/pathfinder
|
||||
actions_types = list(/datum/action/item_action/mod_recall)
|
||||
/// The pathfinder module we are linked to.
|
||||
var/obj/item/mod/module/pathfinder/module
|
||||
/// The jet icon we apply to the MOD.
|
||||
var/image/jet_icon
|
||||
/// List of turfs through which a mod 'steps' to reach the waypoint
|
||||
var/list/path = list()
|
||||
/// The target turf we are after
|
||||
var/turf/target
|
||||
/// How many times have we tried to move?
|
||||
var/tries = 0
|
||||
|
||||
|
||||
/obj/item/implant/mod/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!istype(loc, /obj/item/mod/module/pathfinder))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
module = loc
|
||||
jet_icon = image(icon = 'icons/obj/clothing/modsuit/mod_modules.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER)
|
||||
|
||||
/obj/item/implant/mod/Destroy()
|
||||
if(path)
|
||||
end_recall(successful = FALSE)
|
||||
module = null
|
||||
jet_icon = null
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/mod/proc/recall()
|
||||
target = get_turf(imp_in)
|
||||
if(!module?.mod)
|
||||
to_chat(imp_in, "<span class='warning'>Module is not attached to a suit!</span>")
|
||||
return FALSE
|
||||
if(module.mod.open)
|
||||
to_chat(imp_in, "<span class='warning'>Suit is open!</span>")
|
||||
return FALSE
|
||||
if(length(path))
|
||||
to_chat(imp_in, "<span class='warning'>Suit is already on the way!</span>")
|
||||
return FALSE
|
||||
if(ismob(get_atom_on_turf(module.mod)))
|
||||
to_chat(imp_in, "<span class='warning'>Suit is being worn!</span>")
|
||||
return FALSE
|
||||
if(module.mod.loc != get_turf(module.mod))
|
||||
to_chat(imp_in, "<span class='warning'>Suit contained inside of something!</span>")
|
||||
return FALSE
|
||||
if(module.z != z || get_dist(imp_in, module.mod) > 150)
|
||||
to_chat(imp_in, "<span class='warning'>Suit is too far away!</span>")
|
||||
return FALSE
|
||||
if(!ishuman(imp_in)) //Need to be specific
|
||||
to_chat(imp_in, "<span class='warning'>The implant does not recognize you as a known species!</span>")
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = imp_in
|
||||
set_path(get_path_to(module.mod, target, 150, id = H.wear_id, simulated_only = FALSE)) //Yes, science proves jetpacks work in space. More at 11.
|
||||
if(!length(path)) //Cannot reach target. Give up and announce the issue.
|
||||
to_chat(H, "<span class='warning'>No viable path found!</span>")
|
||||
return FALSE
|
||||
to_chat(H, "<span class='notice>Suit on route!</span>")
|
||||
animate(module.mod, 0.2 SECONDS, pixel_x = 0, pixel_y = 0)
|
||||
module.mod.add_overlay(jet_icon)
|
||||
RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
||||
mod_move(target)
|
||||
return TRUE
|
||||
|
||||
/obj/item/implant/mod/proc/set_path(list/newpath)
|
||||
if(newpath == null)
|
||||
end_recall(FALSE)
|
||||
path = newpath ? newpath : list()
|
||||
|
||||
/obj/item/implant/mod/proc/end_recall(successful = TRUE)
|
||||
if(!module?.mod)
|
||||
return
|
||||
module.mod.cut_overlay(jet_icon)
|
||||
module.mod.transform = matrix()
|
||||
UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
|
||||
if(!successful)
|
||||
to_chat(imp_in, "<span class='warning'>Lost connection to suit!</span>")
|
||||
path = list() //Stopping endless end_recall with luck.
|
||||
|
||||
/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/matrix/mod_matrix = matrix()
|
||||
mod_matrix.Turn(get_angle(source, imp_in))
|
||||
source.transform = mod_matrix
|
||||
|
||||
/obj/item/implant/mod/proc/mod_move(dest)
|
||||
dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
|
||||
if(get_turf(module.mod) == dest) //We have arrived, no need to move again.
|
||||
for(var/mob/living/carbon/human/H in range(1, module.mod))
|
||||
if(H == imp_in)
|
||||
module.attach(imp_in)
|
||||
end_recall()
|
||||
return TRUE
|
||||
end_recall(FALSE)
|
||||
return FALSE
|
||||
|
||||
|
||||
if(!dest || !path || !length(path)) //A-star failed or a path/destination was not set.
|
||||
set_path(null)
|
||||
return FALSE
|
||||
|
||||
var/turf/last_node = get_turf(path[length(path)]) //This is the turf at the end of the path, it should be equal to dest.
|
||||
if(dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
|
||||
set_path(null)
|
||||
return FALSE
|
||||
|
||||
var/step_count = 3 //Temp speed for now
|
||||
|
||||
if(step_count >= 1 && tries < 5)
|
||||
for(var/step_number in 1 to step_count)
|
||||
// Hopefully this wont fill the buckets too much
|
||||
addtimer(CALLBACK(src, PROC_REF(mod_step)), 2 * (step_number - 1))
|
||||
if(tries >= 5)
|
||||
set_path(null)
|
||||
var/target = get_turf(imp_in)
|
||||
var/mob/living/carbon/human/H = imp_in
|
||||
set_path(get_path_to(module.mod, target, 150, id = H.wear_id, simulated_only = FALSE)) //Yes, science proves jetpacks work in space. More at 11.
|
||||
addtimer(CALLBACK(src, PROC_REF(mod_move), target), 6) //I'll value this properly soon
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/implant/mod/proc/mod_step() //Step,increase tries if failed
|
||||
if(!path || !length(path))
|
||||
return FALSE
|
||||
for(var/obj/machinery/door/D in range(2, module.mod))
|
||||
if(D.operating || D.emagged)
|
||||
continue
|
||||
if(D.requiresID() && D.allowed(imp_in))
|
||||
if(D.density)
|
||||
D.open()
|
||||
|
||||
|
||||
if(!step_towards(module.mod, path[1]))
|
||||
tries++
|
||||
return FALSE
|
||||
|
||||
increment_path()
|
||||
tries = 0
|
||||
return TRUE
|
||||
|
||||
/obj/item/implant/mod/proc/increment_path()
|
||||
if(!path || !length(path))
|
||||
return
|
||||
path.Cut(1, 2)
|
||||
|
||||
/datum/action/item_action/mod_recall
|
||||
name = "Recall MOD"
|
||||
desc = "Recall a MODsuit anyplace, anytime."
|
||||
use_itemicon = FALSE
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
button_icon_state = "recall"
|
||||
background_icon_state = "bg_mod"
|
||||
icon_icon = 'icons/mob/actions/actions_mod.dmi'
|
||||
button_icon = 'icons/mob/actions/actions_mod.dmi'
|
||||
/// The cooldown for the recall.
|
||||
COOLDOWN_DECLARE(recall_cooldown)
|
||||
|
||||
/datum/action/item_action/mod_recall/New(Target)
|
||||
..()
|
||||
if(!istype(Target, /obj/item/implant/mod))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/datum/action/item_action/mod_recall/Trigger(left_click)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/obj/item/implant/mod/implant = target
|
||||
if(!COOLDOWN_FINISHED(src, recall_cooldown))
|
||||
to_chat(usr, "<span class='warning'>On cooldown!</span>")
|
||||
return
|
||||
if(implant.recall())
|
||||
COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
|
||||
@@ -0,0 +1,408 @@
|
||||
//Antag modules for MODsuits
|
||||
|
||||
///Armor Booster - Grants your suit more armor and speed in exchange for EVA protection. Also acts as a welding screen.
|
||||
/obj/item/mod/module/armor_booster
|
||||
name = "MOD armor booster module"
|
||||
desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \
|
||||
giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \
|
||||
However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \
|
||||
so this extra armor provides zero ability for extravehicular activity while deployed."
|
||||
icon_state = "armor_booster"
|
||||
module_type = MODULE_TOGGLE
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
removable = FALSE
|
||||
incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
overlay_state_inactive = "module_armorbooster_off"
|
||||
overlay_state_active = "module_armorbooster_on"
|
||||
use_mod_colors = TRUE
|
||||
/// Whether or not this module removes pressure protection.
|
||||
var/remove_pressure_protection = TRUE
|
||||
/// Speed added to the control unit.
|
||||
var/speed_added = 0.5
|
||||
/// Speed that we actually added.
|
||||
var/actual_speed_added = 0
|
||||
/// Armor values added to the suit parts.
|
||||
var/armor_mod_1 = /obj/item/mod/armor/mod_module_armor_boost
|
||||
/// the actual armor object
|
||||
var/obj/item/mod/armor/armor_mod_2 = null
|
||||
/// List of parts of the suit that are spaceproofed, for giving them back the pressure protection.
|
||||
var/list/spaceproofed = list()
|
||||
|
||||
/obj/item/mod/module/armor_booster/Initialize(mapload)
|
||||
. = ..()
|
||||
armor_mod_2 = new armor_mod_1
|
||||
|
||||
/obj/item/mod/module/armor_booster/Destroy()
|
||||
QDEL_NULL(armor_mod_2)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/armor/mod_module_armor_boost
|
||||
armor = list(MELEE = 25, BULLET = 30, LASER = 15, ENERGY = 15, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
|
||||
|
||||
/obj/item/mod/module/armor_booster/on_suit_activation()
|
||||
mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
|
||||
|
||||
/obj/item/mod/module/armor_booster/on_suit_deactivation(deleting = FALSE)
|
||||
if(deleting)
|
||||
return
|
||||
mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
|
||||
|
||||
/obj/item/mod/module/armor_booster/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
||||
to_chat(mod.wearer, "<span class='notice'>Armor deployed, EVA disabled, speed increased.</span>")
|
||||
actual_speed_added = max(0, min(mod.slowdown_active, speed_added / 5))
|
||||
var/list/parts = mod.mod_parts + mod
|
||||
for(var/obj/item/part as anything in parts)
|
||||
part.armor = part.armor.attachArmor(armor_mod_2.armor)
|
||||
part.slowdown -= actual_speed_added
|
||||
if(!remove_pressure_protection || !isclothing(part))
|
||||
continue
|
||||
var/obj/item/clothing/clothing_part = part
|
||||
if(clothing_part.flags & STOPSPRESSUREDMAGE)
|
||||
clothing_part.flags &= ~STOPSPRESSUREDMAGE
|
||||
spaceproofed[clothing_part] = TRUE
|
||||
|
||||
/obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!deleting)
|
||||
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
||||
to_chat(mod.wearer, "<span class='notice'>Armor retracted, EVA enabled, speed decreased.</span>")
|
||||
var/list/parts = mod.mod_parts + mod
|
||||
for(var/obj/item/part as anything in parts)
|
||||
part.armor = part.armor.detachArmor(armor_mod_2.armor)
|
||||
part.slowdown += actual_speed_added
|
||||
if(!remove_pressure_protection || !isclothing(part))
|
||||
continue
|
||||
var/obj/item/clothing/clothing_part = part
|
||||
if(spaceproofed[clothing_part])
|
||||
clothing_part.flags |= STOPSPRESSUREDMAGE
|
||||
spaceproofed = list()
|
||||
|
||||
/obj/item/mod/module/armor_booster/generate_worn_overlay(user, mutable_appearance/standing)
|
||||
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
|
||||
overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
|
||||
return ..()
|
||||
|
||||
///Insignia - Gives you a skin specific stripe.
|
||||
/obj/item/mod/module/insignia
|
||||
name = "MOD insignia module"
|
||||
desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
|
||||
the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
|
||||
they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
|
||||
color patterns to and from the suit."
|
||||
icon_state = "insignia"
|
||||
removable = FALSE
|
||||
incompatible_modules = list(/obj/item/mod/module/insignia)
|
||||
overlay_state_inactive = "module_insignia"
|
||||
|
||||
/obj/item/mod/module/insignia/generate_worn_overlay(user, mutable_appearance/standing)
|
||||
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
|
||||
. = ..()
|
||||
for(var/mutable_appearance/appearance as anything in .)
|
||||
appearance.color = color
|
||||
|
||||
/obj/item/mod/module/insignia/commander
|
||||
color = "#4980a5"
|
||||
|
||||
/obj/item/mod/module/insignia/security
|
||||
color = "#b30d1e"
|
||||
|
||||
/obj/item/mod/module/insignia/engineer
|
||||
color = "#e9c80e"
|
||||
|
||||
/obj/item/mod/module/insignia/medic
|
||||
color = "#ebebf5"
|
||||
|
||||
/obj/item/mod/module/insignia/janitor
|
||||
color = "#7925c7"
|
||||
|
||||
/obj/item/mod/module/insignia/clown
|
||||
color = "#ff1fc7"
|
||||
|
||||
/obj/item/mod/module/insignia/chaplain
|
||||
color = "#f0a00c"
|
||||
|
||||
///Anti Slip - Prevents you from slipping on water.
|
||||
/obj/item/mod/module/noslip
|
||||
name = "MOD anti slip module"
|
||||
desc = "These are a modified variant of standard magnetic boots, utilizing piezoelectric crystals on the soles. \
|
||||
The two plates on the bottom of the boots automatically extend and magnetize as the user steps; \
|
||||
a pull that's too weak to offer them the ability to affix to a hull, but just strong enough to \
|
||||
protect against the fact that you didn't read the wet floor sign. Honk Co. has come out numerous times \
|
||||
in protest of these modules being legal."
|
||||
icon_state = "noslip"
|
||||
complexity = 1
|
||||
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
|
||||
incompatible_modules = list(/obj/item/mod/module/noslip)
|
||||
origin_tech = "syndicate=1"
|
||||
|
||||
/obj/item/mod/module/noslip/on_suit_activation()
|
||||
mod.boots.flags |= NOSLIP
|
||||
|
||||
/obj/item/mod/module/noslip/on_suit_deactivation(deleting = FALSE)
|
||||
mod.boots.flags ^= NOSLIP
|
||||
|
||||
//Bite of 87 Springlock - Equips faster, disguised as DNA lock, can block retracting for 10 seconds.
|
||||
/obj/item/mod/module/springlock/bite_of_87
|
||||
activation_step_time_booster = 10
|
||||
nineteen_eighty_seven_edition = TRUE
|
||||
dont_let_you_come_back = TRUE
|
||||
|
||||
/obj/item/mod/module/springlock/bite_of_87/Initialize(mapload)
|
||||
. = ..()
|
||||
var/obj/item/mod/module/dna_lock/the_dna_lock_behind_the_slaughter = /obj/item/mod/module/dna_lock
|
||||
name = initial(the_dna_lock_behind_the_slaughter.name)
|
||||
desc = initial(the_dna_lock_behind_the_slaughter.desc)
|
||||
icon_state = initial(the_dna_lock_behind_the_slaughter.icon_state)
|
||||
complexity = initial(the_dna_lock_behind_the_slaughter.complexity)
|
||||
use_power_cost = initial(the_dna_lock_behind_the_slaughter.use_power_cost)
|
||||
|
||||
/obj/item/mod/module/holster/hidden/Initialize(mapload)
|
||||
. = ..()
|
||||
var/obj/item/mod/module/tether/fake = /obj/item/mod/module/tether
|
||||
name = initial(fake.name)
|
||||
desc = initial(fake.desc)
|
||||
icon_state = initial(fake.icon_state)
|
||||
complexity = initial(fake.complexity) //This is 1 less complex than a holster, but that is fine tbh, paying tc for it.
|
||||
use_power_cost = initial(fake.use_power_cost)
|
||||
|
||||
///Power kick - Lets the user launch themselves at someone to kick them.
|
||||
/obj/item/mod/module/power_kick
|
||||
name = "MOD power kick module"
|
||||
desc = "This module uses high-power myomer to generate an incredible amount of energy, transferred into the power of a kick."
|
||||
icon_state = "power_kick"
|
||||
module_type = MODULE_ACTIVE
|
||||
removable = FALSE
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
|
||||
incompatible_modules = list(/obj/item/mod/module/power_kick)
|
||||
cooldown_time = 5 SECONDS
|
||||
/// Damage on kick.
|
||||
var/damage = 20
|
||||
/// How long we knockdown for on the kick.
|
||||
var/knockdown_time = 6 SECONDS
|
||||
|
||||
/obj/item/mod/module/power_kick/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(mod.wearer.buckled)
|
||||
return
|
||||
mod.wearer.visible_message("<span class='warning'>[mod.wearer] starts charging a kick!</span>")
|
||||
playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
|
||||
animate(mod.wearer, 0.3 SECONDS, pixel_z = 16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT)
|
||||
addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, SpinAnimation), 3, 2), 0.3 SECONDS)
|
||||
if(!do_after(mod.wearer, 1 SECONDS, target = mod.wearer))
|
||||
animate(mod.wearer, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_IN)
|
||||
return
|
||||
animate(mod.wearer)
|
||||
drain_power(use_power_cost)
|
||||
playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
|
||||
var/angle = get_angle(mod.wearer, target) + 180
|
||||
mod.wearer.transform = mod.wearer.transform.Turn(angle)
|
||||
RegisterSignal(mod.wearer, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact))
|
||||
mod.wearer.apply_status_effect(STATUS_EFFECT_IMPACT_IMMUNE)
|
||||
mod.wearer.throw_at(target, range = 7, speed = 2, thrower = mod.wearer, spin = FALSE, callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle))
|
||||
|
||||
/obj/item/mod/module/power_kick/proc/on_throw_end(mob/living/user, angle)
|
||||
if(!user)
|
||||
return
|
||||
user.transform = user.transform.Turn(angle)
|
||||
animate(user, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_IN)
|
||||
user.remove_status_effect((STATUS_EFFECT_IMPACT_IMMUNE))
|
||||
|
||||
/obj/item/mod/module/power_kick/proc/on_throw_impact(mob/living/source, atom/target, datum/thrownthing/thrownthing)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
|
||||
if(!mod?.wearer)
|
||||
return
|
||||
if(isliving(target))
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_damage(damage, BRUTE, mod.wearer.zone_selected)
|
||||
living_target.KnockDown(knockdown_time)
|
||||
mod.wearer.visible_message("<span class='danger'>[mod.wearer] crashes into [target], knocking them over!</span>", "<span class='userdanger'>You violently crash into [target]!</span>")
|
||||
else
|
||||
return
|
||||
mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH)
|
||||
|
||||
///Plate Compression - Compresses the suit to normal size
|
||||
/obj/item/mod/module/plate_compression
|
||||
name = "MOD plate compression module"
|
||||
desc = "A module that keeps the suit in a very tightly fit state, lowering the overall size. \
|
||||
Due to the pressure on all the parts, typical storage modules do not fit."
|
||||
icon_state = "plate_compression"
|
||||
complexity = 2
|
||||
incompatible_modules = list(/obj/item/mod/module/plate_compression, /obj/item/mod/module/storage)
|
||||
/// The size we set the suit to.
|
||||
var/new_size = WEIGHT_CLASS_NORMAL
|
||||
/// The suit's size before the module is installed.
|
||||
var/old_size
|
||||
origin_tech = "materials=6;bluespace=5;syndicate=1" //Printable at illegals 2, so only one level.
|
||||
|
||||
/obj/item/mod/module/plate_compression/on_install()
|
||||
old_size = mod.w_class
|
||||
mod.w_class = new_size
|
||||
|
||||
/obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE)
|
||||
mod.w_class = old_size
|
||||
old_size = null
|
||||
if(!mod.loc)
|
||||
return
|
||||
mod.forceMove(drop_location())
|
||||
|
||||
|
||||
//Ninja modules for MODsuits
|
||||
|
||||
///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked.
|
||||
/obj/item/mod/module/stealth
|
||||
name = "MOD prototype cloaking module"
|
||||
desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \
|
||||
to bend light around the user, as well as mimetic materials to make the surface of the suit match the \
|
||||
surroundings based off sensor data. For some reason, this tech is rarely seen."
|
||||
icon_state = "cloak"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 4
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 2
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 10
|
||||
incompatible_modules = list(/obj/item/mod/module/stealth)
|
||||
cooldown_time = 5 SECONDS
|
||||
origin_tech = "combat=6;materials=6;powerstorage=5;bluespace=5;syndicate=2" //Printable at 3
|
||||
/// Whether or not the cloak turns off on bumping.
|
||||
var/bumpoff = TRUE
|
||||
/// The alpha applied when the cloak is on.
|
||||
var/stealth_alpha = 50
|
||||
|
||||
/obj/item/mod/module/stealth/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(bumpoff)
|
||||
RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, PROC_REF(unstealth))
|
||||
RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
|
||||
RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) //TODO QWERTY: A LOT OF THESE SIGNALS AINT TRIGGERING. or at least this one.
|
||||
RegisterSignals(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW), PROC_REF(unstealth))
|
||||
animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(bumpoff)
|
||||
UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
|
||||
UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW))
|
||||
animate(mod.wearer, alpha = 255, time = 1.5 SECONDS)
|
||||
|
||||
/obj/item/mod/module/stealth/proc/unstealth(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
to_chat(mod.wearer, "<span class='warning'>[src] gets discharged from contact!</span>")
|
||||
do_sparks(2, TRUE, src)
|
||||
drain_power(use_power_cost)
|
||||
on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
|
||||
/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!isliving(target))
|
||||
return
|
||||
unstealth(source)
|
||||
|
||||
/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/item/projectile)
|
||||
SIGNAL_HANDLER
|
||||
unstealth(source)
|
||||
|
||||
//Advanced Cloaking - Doesn't turf off on bump, less power drain, more stealthy.
|
||||
/obj/item/mod/module/stealth/ninja
|
||||
name = "MOD advanced cloaking module"
|
||||
desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \
|
||||
The field has been tuned to be even more responsive and fast-acting, with enough stability to \
|
||||
continue operation of the field even if the user bumps into others. \
|
||||
The power draw has been reduced drastically, making this perfect for activities like \
|
||||
standing near sentry turrets for extended periods of time."
|
||||
icon_state = "cloak_ninja"
|
||||
bumpoff = FALSE
|
||||
stealth_alpha = 10
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
|
||||
cooldown_time = 3 SECONDS
|
||||
origin_tech = "combat=6;materials=6;powerstorage=6;bluespace=6;syndicate=4"
|
||||
|
||||
///Status Readout - Puts a lot of information including health, nutrition, fingerprints, temperature to the suit TGUI.
|
||||
/obj/item/mod/module/status_readout
|
||||
name = "MOD status readout module"
|
||||
desc = "A once-common module, this technology went unfortunately out of fashion; \
|
||||
and right into the arachnid grip of the Spider Clan. This hooks into the suit's spine, \
|
||||
capable of capturing and displaying all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \
|
||||
and even useful information such as their overall health and wellness. \
|
||||
The syndicate has been seen using this module of late, with NT as well getting into the technology on their elitest of suits."
|
||||
icon_state = "status"
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
|
||||
incompatible_modules = list(/obj/item/mod/module/status_readout)
|
||||
tgui_id = "status_readout"
|
||||
origin_tech = "combat=6;biotech=6;syndicate=1"
|
||||
|
||||
/obj/item/mod/module/status_readout/add_ui_data()
|
||||
. = ..()
|
||||
.["statustime"] = station_time_timestamp()
|
||||
.["statusid"] = GLOB.round_id
|
||||
.["statushealth"] = mod.wearer?.health || 0
|
||||
.["statusmaxhealth"] = mod.wearer?.getMaxHealth() || 0
|
||||
.["statusbrute"] = mod.wearer?.getBruteLoss() || 0
|
||||
.["statusburn"] = mod.wearer?.getFireLoss() || 0
|
||||
.["statustoxin"] = mod.wearer?.getToxLoss() || 0
|
||||
.["statusoxy"] = mod.wearer?.getOxyLoss() || 0
|
||||
.["statustemp"] = mod.wearer?.bodytemperature || 0
|
||||
.["statusnutrition"] = mod.wearer?.nutrition || 0
|
||||
.["statusfingerprints"] = mod.wearer ? md5(mod.wearer.dna.unique_enzymes) : null
|
||||
.["statusdna"] = mod.wearer?.dna.unique_enzymes
|
||||
.["statusviruses"] = null
|
||||
if(!length(mod.wearer?.viruses))
|
||||
return
|
||||
var/list/viruses = list()
|
||||
for(var/datum/disease/virus as anything in mod.wearer.viruses)
|
||||
var/list/virus_data = list()
|
||||
virus_data["name"] = virus.name
|
||||
virus_data["type"] = virus.spread_text
|
||||
virus_data["stage"] = virus.stage
|
||||
virus_data["maxstage"] = virus.max_stages
|
||||
virus_data["cure"] = virus.cure_text
|
||||
viruses += list(virus_data)
|
||||
.["statusviruses"] = viruses
|
||||
|
||||
///Camera Module - Puts a camera in the modsuit that the ERT commander can see
|
||||
/obj/item/mod/module/ert_camera
|
||||
name = "MOD camera module"
|
||||
desc = "This combination camera and broadcasting module grants the modsuit a camera that tracks what the user see, and sends it to the nearest station and \
|
||||
CC blackbox. This is used for ERT commander tracking, performance review, Nanotrasen's Funniest Home Videos, \
|
||||
and used for reference for their Deathsquad Cartoon Series."
|
||||
icon_state = "eradicationlock" //looks like a bluespace transmitter or something, probably could use an actual camera look.
|
||||
complexity = 1
|
||||
incompatible_modules = list(/obj/item/mod/module/ert_camera)
|
||||
var/obj/machinery/camera/portable/camera
|
||||
|
||||
/obj/item/mod/module/ert_camera/on_suit_activation()
|
||||
if(ishuman(mod.wearer))
|
||||
register_camera(mod.wearer)
|
||||
|
||||
/obj/item/mod/module/ert_camera/proc/register_camera(mob/wearer)
|
||||
if(camera)
|
||||
return
|
||||
camera = new /obj/machinery/camera/portable(src, FALSE)
|
||||
camera.network = list("ERT")
|
||||
camera.c_tag = wearer.name
|
||||
to_chat(wearer, "<span class='notice'>User scanned as [camera.c_tag]. Camera activated.</span>")
|
||||
|
||||
/obj/item/mod/module/ert_camera/Destroy()
|
||||
QDEL_NULL(camera)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/ert_camera/on_suit_deactivation(deleting = FALSE)
|
||||
QDEL_NULL(camera)
|
||||
@@ -0,0 +1,153 @@
|
||||
//Engineering modules for MODsuits
|
||||
|
||||
///Welding Protection - Makes the helmet protect from flashes and welding.
|
||||
/obj/item/mod/module/welding
|
||||
name = "MOD welding protection module"
|
||||
desc = "A module installed into the visor of the suit, this projects a \
|
||||
polarized, holographic overlay in front of the user's eyes. It's rated high enough for \
|
||||
immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights."
|
||||
icon_state = "welding"
|
||||
complexity = 1
|
||||
incompatible_modules = list(/obj/item/mod/module/welding, /obj/item/mod/module/armor_booster)
|
||||
overlay_state_inactive = "module_welding"
|
||||
|
||||
/obj/item/mod/module/welding/on_suit_activation()
|
||||
mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
|
||||
|
||||
/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE)
|
||||
if(deleting)
|
||||
return
|
||||
mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
|
||||
|
||||
///T-Ray Scan - Scans the terrain for undertile objects.
|
||||
/obj/item/mod/module/t_ray
|
||||
name = "MOD t-ray scan module"
|
||||
desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \
|
||||
to essentially echolocate things beneath the floor, mostly cables and pipes. \
|
||||
A staple of atmospherics work, and counter-smuggling work."
|
||||
icon_state = "tray"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
incompatible_modules = list(/obj/item/mod/module/t_ray)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
/// T-ray scan range.
|
||||
var/range = 4
|
||||
|
||||
/obj/item/mod/module/t_ray/on_active_process()
|
||||
t_ray_scan(mod.wearer, 0.8 SECONDS, range)
|
||||
|
||||
///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips.
|
||||
/obj/item/mod/module/magboot
|
||||
name = "MOD magnetic stability module"
|
||||
desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \
|
||||
excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \
|
||||
However, these basic models do not feature computerized systems to automatically toggle them on and off, \
|
||||
so numerous users report a certain stickiness to their steps."
|
||||
icon_state = "magnet"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 2
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
incompatible_modules = list(/obj/item/mod/module/magboot)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
/// Slowdown added onto the suit.
|
||||
var/slowdown_active = 0.5
|
||||
|
||||
/obj/item/mod/module/magboot/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
mod.boots.flags |= NOSLIP
|
||||
mod.slowdown += slowdown_active
|
||||
mod.boots.magbooted = TRUE
|
||||
|
||||
/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
mod.boots.flags ^= NOSLIP
|
||||
mod.slowdown -= slowdown_active
|
||||
mod.boots.magbooted = FALSE
|
||||
|
||||
/obj/item/mod/module/magboot/advanced
|
||||
name = "MOD advanced magnetic stability module"
|
||||
removable = FALSE
|
||||
complexity = 0
|
||||
slowdown_active = 0
|
||||
|
||||
///Radiation Protection - Gives the user rad info in the ui, currently
|
||||
/obj/item/mod/module/rad_protection
|
||||
name = "MOD radiation detector module"
|
||||
desc = "A protoype module that improves the sensors on the modsuit to detect radiation on the user. \
|
||||
Currently due to time restraints and a lack of lead on lavaland, it does not have a built in geiger counter or radiation protection."
|
||||
icon_state = "radshield"
|
||||
complexity = 0 //I'm setting this to zero for now due to it not currently increasing radiaiton armor. If we add giger counter / additional rad protecion to this, it should be 2. We denied radiation potions before, so this should NOT give full rad immunity on a engi modsuit
|
||||
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 //Lowered from 0.3 due to no protection.
|
||||
incompatible_modules = list(/obj/item/mod/module/rad_protection)
|
||||
tgui_id = "rad_counter"
|
||||
|
||||
/obj/item/mod/module/rad_protection/add_ui_data()
|
||||
. = ..()
|
||||
.["userradiated"] = mod.wearer?.radiation || 0
|
||||
.["usertoxins"] = mod.wearer?.getToxLoss() || 0
|
||||
.["usermaxtoxins"] = mod.wearer?.getMaxHealth() || 0
|
||||
|
||||
|
||||
///Emergency Tether - Shoots a grappling hook projectile in 0g that throws the user towards it.
|
||||
/obj/item/mod/module/tether
|
||||
name = "MOD emergency tether module"
|
||||
desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
|
||||
While some older models of cargo-oriented grapples have capacities of a few tons, \
|
||||
these are only capable of working in zero-gravity environments, a blessing to some Engineers."
|
||||
icon_state = "tether"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN
|
||||
incompatible_modules = list(/obj/item/mod/module/tether)
|
||||
cooldown_time = 4 SECONDS
|
||||
|
||||
/obj/item/mod/module/tether/on_use()
|
||||
if(has_gravity(get_turf(src)))
|
||||
to_chat(mod.wearer, "<span class='warning'>Too much gravity to use the tether!</span>")
|
||||
playsound(src, 'sound/weapons/gun_interactions/dry_fire.ogg', 25, TRUE)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/tether/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/obj/item/projectile/tether = new /obj/item/projectile/tether(get_turf(mod.wearer))
|
||||
tether.original = target
|
||||
tether.firer = mod.wearer
|
||||
tether.preparePixelProjectile(target, get_turf(target), mod.wearer)
|
||||
tether.fire()
|
||||
playsound(src, 'sound/weapons/batonextend.ogg', 25, TRUE)
|
||||
INVOKE_ASYNC(tether, TYPE_PROC_REF(/obj/item/projectile/tether, make_chain))
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/projectile/tether
|
||||
name = "tether"
|
||||
icon_state = "tether_projectile"
|
||||
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
||||
speed = 2
|
||||
damage = 5
|
||||
range = 15
|
||||
hitsound = 'sound/weapons/batonextend.ogg'
|
||||
hitsound_wall = 'sound/weapons/batonextend.ogg'
|
||||
|
||||
/obj/item/projectile/tether/proc/make_chain()
|
||||
if(firer)
|
||||
chain = Beam(firer, icon_state = "line", icon = 'icons/obj/clothing/modsuit/mod_modules.dmi', time = 10 SECONDS, maxdistance = 15)
|
||||
|
||||
/obj/item/projectile/tether/on_hit(atom/target)
|
||||
. = ..()
|
||||
if(firer && isliving(firer))
|
||||
var/mob/living/L = firer
|
||||
L.apply_status_effect(STATUS_EFFECT_IMPACT_IMMUNE)
|
||||
L.throw_at(target, 15, 1, L, FALSE, FALSE, callback = CALLBACK(L, TYPE_PROC_REF(/mob/living, remove_status_effect), STATUS_EFFECT_IMPACT_IMMUNE))
|
||||
|
||||
/obj/item/projectile/tether/Destroy()
|
||||
QDEL_NULL(chain)
|
||||
return ..()
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
//General modules for MODsuits
|
||||
|
||||
///Storage - Adds a storage component to the suit.
|
||||
/obj/item/mod/module/storage
|
||||
name = "MOD storage module"
|
||||
desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \
|
||||
the surface of the suit, useful for storing various bits, and or bobs."
|
||||
icon_state = "storage"
|
||||
complexity = 3
|
||||
incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression)
|
||||
/// Max weight class of items in the storage.
|
||||
var/max_w_class = WEIGHT_CLASS_NORMAL
|
||||
/// Max combined weight of all items in the storage.
|
||||
var/max_combined_w_class = 15
|
||||
/// Max amount of items in the storage.
|
||||
var/max_items = 7
|
||||
var/obj/item/storage/backpack/modstorage/bag
|
||||
|
||||
/obj/item/mod/module/storage/Initialize(mapload)
|
||||
. = ..()
|
||||
var/obj/item/storage/backpack/modstorage/S = new(src)
|
||||
bag = S
|
||||
bag.max_w_class = max_w_class
|
||||
bag.max_combined_w_class = max_combined_w_class
|
||||
bag.storage_slots = max_items
|
||||
bag.source = src
|
||||
|
||||
/obj/item/mod/module/storage/Destroy()
|
||||
QDEL_NULL(bag)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/mod/module/storage/on_install()
|
||||
mod.bag = bag
|
||||
bag.forceMove(mod)
|
||||
|
||||
/obj/item/mod/module/storage/on_uninstall(deleting = FALSE)
|
||||
if(!deleting)
|
||||
for(var/obj/I in bag.contents)
|
||||
I.forceMove(get_turf(loc))
|
||||
bag.forceMove(src)
|
||||
mod.bag = null
|
||||
return
|
||||
qdel(bag)
|
||||
UnregisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP)
|
||||
|
||||
/obj/item/mod/module/storage/on_suit_deactivation(deleting)
|
||||
. = ..()
|
||||
bag.forceMove(src) //So the pinpointer doesnt lie.
|
||||
|
||||
/obj/item/mod/module/storage/on_unequip()
|
||||
. = ..()
|
||||
bag.forceMove(src)
|
||||
|
||||
/obj/item/mod/module/storage/large_capacity
|
||||
name = "MOD expanded storage module"
|
||||
desc = "Reverse engineered by Cybersun Industries from Donk Corporation designs, this system of hidden compartments \
|
||||
is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \
|
||||
whether smuggling, or simply hauling."
|
||||
icon_state = "storage_large"
|
||||
max_combined_w_class = 21
|
||||
max_items = 14
|
||||
|
||||
/obj/item/mod/module/storage/syndicate
|
||||
name = "MOD syndicate storage module"
|
||||
desc = "A storage system using nanotechnology developed by Donk Corporation, these compartments use \
|
||||
esoteric technology to compress the physical matter of items put inside of them, \
|
||||
essentially shrinking items for much easier and more portable storage."
|
||||
icon_state = "storage_syndi"
|
||||
max_combined_w_class = 30
|
||||
max_items = 21
|
||||
origin_tech = "materials=6;bluespace=5;syndicate=2"
|
||||
|
||||
/obj/item/mod/module/storage/belt
|
||||
name = "MOD case storage module"
|
||||
desc = "Some concessions had to be made when creating a compressed modular suit core. \
|
||||
As a result, Roseus Galactic equipped their suit with a slimline storage case. \
|
||||
If you find this equipped to a standard modular suit, then someone has almost certainly shortchanged you on a proper storage module."
|
||||
icon_state = "storage_case"
|
||||
complexity = 0
|
||||
max_w_class = WEIGHT_CLASS_SMALL
|
||||
removable = FALSE
|
||||
max_combined_w_class = 21
|
||||
max_items = 7
|
||||
|
||||
/obj/item/mod/module/storage/bluespace
|
||||
name = "MOD bluespace storage module"
|
||||
desc = "A storage system developed by Nanotrasen, these compartments employ \
|
||||
miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
|
||||
icon_state = "storage_bluespace"
|
||||
max_w_class = WEIGHT_CLASS_GIGANTIC
|
||||
max_combined_w_class = 60
|
||||
max_items = 21
|
||||
|
||||
|
||||
//Internal
|
||||
/obj/item/storage/backpack/modstorage
|
||||
name = "mod's storage"
|
||||
desc = "Either you tried to spawn a storage mod, or someone fucked up. Unless you are an admin that just tried to spawn something, issue report."
|
||||
var/obj/item/mod/module/storage/source
|
||||
|
||||
/obj/item/storage/backpack/modstorage/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/storage/backpack/modstorage/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/storage/backpack/modstorage/process()
|
||||
update_viewers()
|
||||
|
||||
/obj/item/storage/backpack/modstorage/update_viewers()
|
||||
for(var/_M in mobs_viewing)
|
||||
var/mob/M = _M
|
||||
if(!QDELETED(M) && M.s_active == src && (M in range(1, loc)) && (source.mod.loc == _M || (M in range(1, source.mod)))) //This ensures someone isn't taking it away from the mod unit
|
||||
continue
|
||||
hide_from(M)
|
||||
|
||||
|
||||
///Ion Jetpack - Lets the user fly freely through space using battery charge.
|
||||
/obj/item/mod/module/jetpack
|
||||
name = "MOD ion jetpack module"
|
||||
desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
|
||||
Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
|
||||
charge from the suit's charge. Some say this isn't Cybersun Industries's first foray into jet-enabled suits."
|
||||
icon_state = "jetpack"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 3
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN
|
||||
incompatible_modules = list(/obj/item/mod/module/jetpack)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
overlay_state_inactive = "module_jetpack"
|
||||
overlay_state_active = "module_jetpack_on"
|
||||
/// Do we stop the wearer from gliding in space.
|
||||
var/stabilizers = FALSE
|
||||
|
||||
/obj/item/mod/module/jetpack/proc/set_stabilizers(new_stabilizers)
|
||||
if(stabilizers == new_stabilizers)
|
||||
return
|
||||
stabilizers = new_stabilizers
|
||||
|
||||
/obj/item/mod/module/jetpack/get_configuration()
|
||||
. = ..()
|
||||
.["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
|
||||
|
||||
/obj/item/mod/module/jetpack/configure_edit(key, value)
|
||||
switch(key)
|
||||
if("stabilizers")
|
||||
set_stabilizers(text2bool(value))
|
||||
|
||||
/obj/item/mod/module/jetpack/proc/allow_thrust()
|
||||
if(!active)
|
||||
return
|
||||
if(!drain_power(use_power_cost))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/jetpack/proc/get_user()
|
||||
return mod.wearer
|
||||
|
||||
/obj/item/mod/module/jetpack/on_activation()
|
||||
. = ..()
|
||||
mod.jetpack_active = TRUE
|
||||
|
||||
/obj/item/mod/module/jetpack/on_deactivation(display_message, deleting)
|
||||
. = ..()
|
||||
mod.jetpack_active = FALSE
|
||||
|
||||
/obj/item/mod/module/jetpack/advanced
|
||||
name = "MOD advanced ion jetpack module"
|
||||
desc = "An improvement on the previous model of electric thrusters. This one achieves better efficency through \
|
||||
mounting of more jets and a red paint applied on it."
|
||||
icon_state = "jetpack_advanced"
|
||||
overlay_state_inactive = "module_jetpackadv"
|
||||
overlay_state_active = "module_jetpackadv_on"
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.25
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
origin_tech = "materials=4;magnets=4;engineering=5" //To replace the old hardsuit upgrade jetpack levels.
|
||||
|
||||
///EMP Shield - Protects the suit from EMPs.
|
||||
/obj/item/mod/module/emp_shield
|
||||
name = "MOD EMP shield module"
|
||||
desc = "A field inhibitor installed into the suit, protecting it against feedback such as \
|
||||
electromagnetic pulses that would otherwise damage the electronic systems of the suit or it's modules. \
|
||||
However, it will take from the suit's power to do so."
|
||||
icon_state = "empshield"
|
||||
origin_tech = "materials=6;bluespace=5;syndicate=2"
|
||||
complexity = 1
|
||||
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
incompatible_modules = list(/obj/item/mod/module/emp_shield, /obj/item/mod/module/dna_lock)
|
||||
|
||||
/obj/item/mod/module/emp_shield/on_install()
|
||||
mod.emp_proof = TRUE
|
||||
|
||||
/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE)
|
||||
mod.emp_proof = FALSE
|
||||
|
||||
///Flashlight - Gives the suit a customizable flashlight.
|
||||
/obj/item/mod/module/flashlight
|
||||
name = "MOD flashlight module"
|
||||
desc = "A simple pair of configurable flashlights installed on the left and right sides of the helmet, \
|
||||
useful for providing light in a variety of ranges and colors. \
|
||||
Some survivalists prefer the color green for their illumination, for reasons unknown."
|
||||
icon_state = "flashlight"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
incompatible_modules = list(/obj/item/mod/module/flashlight)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
overlay_state_inactive = "module_light"
|
||||
overlay_state_active = "module_light_on"
|
||||
light_color = COLOR_WHITE
|
||||
///The light power for the mod
|
||||
var/mod_light_range = 4
|
||||
///The light range for the mod
|
||||
var/mod_light_power = 2
|
||||
var/light_on = FALSE
|
||||
/// Charge drain per range amount.
|
||||
var/base_power = DEFAULT_CHARGE_DRAIN * 0.1
|
||||
/// Minimum range we can set.
|
||||
var/min_range = 2
|
||||
/// Maximum range we can set.
|
||||
var/max_range = 5
|
||||
|
||||
/obj/item/mod/module/flashlight/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
active_power_cost = base_power * mod_light_range
|
||||
mod.set_light(mod_light_range, mod_light_power, light_color)
|
||||
|
||||
/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
mod.set_light(0, mod_light_power, light_color)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
/obj/item/mod/module/flashlight/on_process()
|
||||
active_power_cost = base_power * mod_light_range
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/flashlight/get_configuration()
|
||||
. = ..()
|
||||
.["light_color"] = add_ui_configuration("Light Color", "color", light_color)
|
||||
.["light_range"] = add_ui_configuration("Light Range", "number", mod_light_range)
|
||||
|
||||
/obj/item/mod/module/flashlight/configure_edit(key, value)
|
||||
switch(key)
|
||||
if("light_color")
|
||||
value = input(usr, "Pick new light color", "Flashlight Color") as color|null
|
||||
if(!value)
|
||||
return
|
||||
if(is_color_dark(value, 50))
|
||||
to_chat(mod.wearer, ("<span class='warning'>That is too dark</span>"))
|
||||
return
|
||||
light_color = value
|
||||
mod.wearer.regenerate_icons()
|
||||
if("light_range")
|
||||
mod_light_range = (clamp(text2num(value), min_range, max_range))
|
||||
mod.set_light(0, mod_light_power, light_color)
|
||||
mod_color_overide = light_color
|
||||
on_deactivation()
|
||||
|
||||
///Dispenser - Dispenses an item after a time passes.
|
||||
/obj/item/mod/module/dispenser
|
||||
name = "MOD burger dispenser module"
|
||||
desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \
|
||||
This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \
|
||||
palm of the wearer's glove; however, research seemed to have entirely stopped at cheeseburgers. \
|
||||
Notably, all attempts to get it to dispense Earl Grey tea have failed."
|
||||
icon_state = "dispenser"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 3
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 2
|
||||
incompatible_modules = list(/obj/item/mod/module/dispenser)
|
||||
cooldown_time = 5 SECONDS
|
||||
/// Path we dispense.
|
||||
var/dispense_type = /obj/item/reagent_containers/food/snacks/cheeseburger
|
||||
/// Time it takes for us to dispense.
|
||||
var/dispense_time = 0 SECONDS
|
||||
|
||||
/obj/item/mod/module/dispenser/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod.wearer))
|
||||
return FALSE
|
||||
var/obj/item/dispensed = new dispense_type(mod.wearer.loc)
|
||||
mod.wearer.put_in_hands(dispensed)
|
||||
playsound(src, 'sound/machines/click.ogg', 100, TRUE)
|
||||
drain_power(use_power_cost)
|
||||
return dispensed
|
||||
|
||||
///Thermal Regulator - Regulates the wearer's core temperature.
|
||||
/obj/item/mod/module/thermal_regulator
|
||||
name = "MOD thermal regulator module"
|
||||
desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \
|
||||
flexible cooling lines. This circulates coolant at various user-controlled temperatures, \
|
||||
ensuring they're comfortable; even if they're some that like it hot."
|
||||
icon_state = "regulator"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
incompatible_modules = list(/obj/item/mod/module/thermal_regulator)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
/// The temperature we are regulating to.
|
||||
var/temperature_setting = BODYTEMP_NORMAL
|
||||
/// Minimum temperature we can set.
|
||||
var/min_temp = 293.15
|
||||
/// Maximum temperature we can set.
|
||||
var/max_temp = 318.15
|
||||
|
||||
/obj/item/mod/module/thermal_regulator/get_configuration()
|
||||
. = ..()
|
||||
.["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C)
|
||||
|
||||
/obj/item/mod/module/thermal_regulator/configure_edit(key, value)
|
||||
switch(key)
|
||||
if("temperature_setting")
|
||||
temperature_setting = clamp(text2num(value) + T0C, min_temp, max_temp)
|
||||
|
||||
/obj/item/mod/module/thermal_regulator/on_active_process()
|
||||
if(mod.wearer.bodytemperature > temperature_setting)
|
||||
mod.wearer.bodytemperature = max(temperature_setting, mod.wearer.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
|
||||
else if(mod.wearer.bodytemperature < temperature_setting)
|
||||
mod.wearer.bodytemperature = min(temperature_setting, mod.wearer.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
|
||||
|
||||
/obj/item/mod/module/dna_lock
|
||||
name = "MOD DNA lock module"
|
||||
desc = "A module which engages with the various locks and seals tied to the suit's systems, \
|
||||
enabling it to only be worn by someone corresponding with the user's exact DNA profile; \
|
||||
however, this incredibly sensitive module is shorted out by EMPs. Luckily, stable mutagen has been outlawed."
|
||||
icon_state = "dnalock"
|
||||
origin_tech = "materials=6;bluespace=5;syndicate=1"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 2
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 3
|
||||
incompatible_modules = list(/obj/item/mod/module/dna_lock, /obj/item/mod/module/emp_shield)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
/// The DNA we lock with.
|
||||
var/dna = null
|
||||
|
||||
/obj/item/mod/module/dna_lock/on_install()
|
||||
RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation))
|
||||
RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal))
|
||||
RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
|
||||
RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag))
|
||||
|
||||
/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE)
|
||||
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
|
||||
UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL)
|
||||
UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT)
|
||||
UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
|
||||
|
||||
/obj/item/mod/module/dna_lock/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
dna = mod.wearer.dna.unique_enzymes
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/dna_lock/emp_act(severity)
|
||||
. = ..()
|
||||
if(mod.emp_proof)
|
||||
return
|
||||
on_emp(src, severity)
|
||||
|
||||
/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||
. = ..()
|
||||
on_emag(src, user, emag_card)
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
|
||||
if(!iscarbon(user))
|
||||
return FALSE
|
||||
if(!dna)
|
||||
return TRUE
|
||||
if(dna == mod.wearer.dna.unique_enzymes)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
dna = null
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
dna = null
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!dna_check(user))
|
||||
atom_say("ERROR: User does not match owner DNA")
|
||||
return MOD_CANCEL_ACTIVATE
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!dna_check(user))
|
||||
atom_say("ERROR: User does not match owner DNA")
|
||||
return MOD_CANCEL_REMOVAL
|
||||
|
||||
/obj/item/mod/module/dna_lock/emp_shield
|
||||
name = "MOD DN-MP shield lock"
|
||||
desc = "This syndicate module is a combination EMP shield and DNA lock. Provides the best of both worlds, with the weakness of niether."
|
||||
icon_state = "dnalock"
|
||||
origin_tech = "materials=6;bluespace=5;syndicate=3"
|
||||
complexity = 3
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
|
||||
|
||||
/obj/item/mod/module/dna_lock/emp_shield/on_install()
|
||||
. = ..()
|
||||
mod.emp_proof = TRUE
|
||||
|
||||
/obj/item/mod/module/dna_lock/emp_shield/on_uninstall(deleting = FALSE)
|
||||
. = ..()
|
||||
mod.emp_proof = FALSE
|
||||
|
||||
///Plasma Stabilizer - Prevents plasmamen from igniting in the suit
|
||||
/obj/item/mod/module/plasma_stabilizer
|
||||
name = "MOD plasma stabilizer module"
|
||||
desc = "This system essentially forms an atmosphere of its own, within the suit, \
|
||||
efficiently and quickly preventing oxygen from causing the user's head to burst into flame. \
|
||||
This allows plasmamen to safely remove their helmet, allowing for easier \
|
||||
equipping of any MODsuit-related equipment, or otherwise. \
|
||||
The purple glass of the visor seems to be constructed for nostalgic purposes."
|
||||
icon_state = "plasma_stabilizer"
|
||||
complexity = 1
|
||||
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
|
||||
overlay_state_inactive = "module_plasma"
|
||||
|
||||
/obj/item/mod/module/plasma_stabilizer/on_equip()
|
||||
ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MODSUIT_TRAIT)
|
||||
|
||||
/obj/item/mod/module/plasma_stabilizer/on_unequip()
|
||||
REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MODSUIT_TRAIT)
|
||||
@@ -0,0 +1,129 @@
|
||||
//Maint modules for MODsuits
|
||||
|
||||
///Springlock Mechanism - allows your modsuit to activate faster, but reagents are very dangerous.
|
||||
/obj/item/mod/module/springlock
|
||||
name = "MOD springlock module"
|
||||
desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \
|
||||
This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \
|
||||
up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \
|
||||
into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you?"
|
||||
icon_state = "springlock"
|
||||
complexity = 3 // it is inside every part of your suit, so
|
||||
incompatible_modules = list(/obj/item/mod/module/springlock)
|
||||
///How much faster will your suit deploy?
|
||||
var/activation_step_time_booster = 2
|
||||
///Is this the syndicate version, which can be toggled on multitool?
|
||||
var/nineteen_eighty_seven_edition = FALSE
|
||||
///If this is true, the suit will prevent you from retracting for 10 seconds, so an antag can smoke bomb you.
|
||||
var/dont_let_you_come_back = FALSE
|
||||
///If this is true, we are about to spring shut on someone, and should not remove the retraction blocking.
|
||||
var/incoming_jumpscare = FALSE
|
||||
|
||||
/obj/item/mod/module/springlock/on_install()
|
||||
mod.activation_step_time *= (1 / activation_step_time_booster)
|
||||
|
||||
/obj/item/mod/module/springlock/on_uninstall(deleting = FALSE)
|
||||
mod.activation_step_time *= activation_step_time_booster
|
||||
|
||||
/obj/item/mod/module/springlock/on_suit_activation()
|
||||
RegisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_wearer_exposed))
|
||||
if(dont_let_you_come_back)
|
||||
RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block))
|
||||
addtimer(CALLBACK(src, PROC_REF(remove_retraction_block)), 10 SECONDS)
|
||||
|
||||
/obj/item/mod/module/springlock/on_suit_deactivation(deleting = FALSE)
|
||||
UnregisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS)
|
||||
|
||||
/obj/item/mod/module/springlock/multitool_act(mob/living/user, obj/item/I)
|
||||
if(!nineteen_eighty_seven_edition)
|
||||
return
|
||||
. = TRUE
|
||||
if(dont_let_you_come_back)
|
||||
to_chat(user, "<span class='notice'>You disable the retraction blocking systems.</span>")
|
||||
dont_let_you_come_back = FALSE
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You enable the retraction blocking systems, which will block people from retracting the modsuit for 10 seconds.</span>")
|
||||
dont_let_you_come_back = TRUE
|
||||
|
||||
|
||||
///Signal fired when wearer is exposed to reagents
|
||||
/obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message)
|
||||
SIGNAL_HANDLER
|
||||
remove_retraction_block() //No double signals
|
||||
to_chat(mod.wearer, "<span class='danger'>[src] makes an ominous click sound...</span>")
|
||||
incoming_jumpscare = TRUE
|
||||
playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE)
|
||||
addtimer(CALLBACK(src, PROC_REF(snap_shut)), rand(3 SECONDS, 5 SECONDS))
|
||||
RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block))
|
||||
|
||||
///Signal fired when wearer attempts to activate/deactivate suits
|
||||
/obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
to_chat(mod.wearer, "<span class='userdanger'>The springlocks aren't responding...?</span>")
|
||||
return MOD_CANCEL_ACTIVATE
|
||||
|
||||
///Removes the retraction blocker from the springlock so long as they are not about to be killed
|
||||
/obj/item/mod/module/springlock/proc/remove_retraction_block()
|
||||
if(!incoming_jumpscare)
|
||||
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
|
||||
|
||||
///Delayed death proc of the suit after the wearer is exposed to reagents
|
||||
/obj/item/mod/module/springlock/proc/snap_shut()
|
||||
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
|
||||
if(!mod.wearer) //while there is a guaranteed user when on_wearer_exposed() fires, that isn't the same case for this proc
|
||||
return
|
||||
mod.wearer.visible_message("<span class='danger'>[src] inside [mod.wearer]'s [mod.name] snaps shut, mutilating the user inside!</span>", "<span class='biggerdanger'><b>*SNAP*</b></span>")
|
||||
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.adjustBruteLoss(1987) //boggers, bogchamp, etc //why not just poggers, also this caps at 595 damage but comedy
|
||||
incoming_jumpscare = FALSE
|
||||
|
||||
///Balloon Blower - Blows a balloon.
|
||||
/obj/item/mod/module/balloon
|
||||
name = "MOD balloon blower module"
|
||||
desc = "A strange module invented years ago by some ingenious mimes. It blows balloons."
|
||||
icon_state = "bloon"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
incompatible_modules = list(/obj/item/mod/module/balloon)
|
||||
cooldown_time = 15 SECONDS
|
||||
|
||||
/obj/item/mod/module/balloon/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!do_after(mod.wearer, 10 SECONDS, target = mod.wearer))
|
||||
return FALSE
|
||||
mod.wearer.adjustOxyLoss(20)
|
||||
playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE)
|
||||
var/obj/item/toy/balloon/balloon = new(get_turf(src))
|
||||
mod.wearer.put_in_hands(balloon)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
|
||||
///Stamper - Extends a stamp that can switch between accept/deny modes.
|
||||
/obj/item/mod/module/stamp
|
||||
name = "MOD stamper module"
|
||||
desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \
|
||||
able to switch between accept and deny modes."
|
||||
icon_state = "stamp"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
device = /obj/item/stamp/mod
|
||||
incompatible_modules = list(/obj/item/mod/module/stamp)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
|
||||
/obj/item/stamp/mod
|
||||
name = "MOD electronic stamp"
|
||||
desc = "A high-power stamp, able to switch between accept and deny mode when used."
|
||||
|
||||
/obj/item/stamp/mod/attack_self(mob/user, modifiers)
|
||||
. = ..()
|
||||
if(icon_state == "stamp-ok")
|
||||
icon_state = "stamp-deny"
|
||||
else
|
||||
icon_state = "stamp-ok"
|
||||
@@ -0,0 +1,115 @@
|
||||
//Medical modules for MODsuits. Not much here, sorry.
|
||||
|
||||
///Injector - Gives the suit an extendable large-capacity piercing syringe.
|
||||
/obj/item/mod/module/injector
|
||||
name = "MOD injector module"
|
||||
desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \
|
||||
with a tip fine enough to locate the emergency injection ports on any suit of armor, \
|
||||
penetrating it with ease. Even yours."
|
||||
icon_state = "injector"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
device = /obj/item/reagent_containers/syringe/mod
|
||||
incompatible_modules = list(/obj/item/mod/module/injector)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
|
||||
/obj/item/reagent_containers/syringe/mod
|
||||
name = "MOD injector syringe"
|
||||
desc = "A high-capacity syringe, with a tip fine enough to locate \
|
||||
the emergency injection ports on any suit of armor, penetrating it with ease. Even yours."
|
||||
amount_per_transfer_from_this = 30
|
||||
possible_transfer_amounts = list(5, 10, 15, 20, 30)
|
||||
volume = 30
|
||||
penetrates_thick = TRUE
|
||||
|
||||
///Defibrillator - Gives the suit an extendable pair of shock paddles.
|
||||
/obj/item/mod/module/defibrillator
|
||||
name = "MOD defibrillator module"
|
||||
desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
|
||||
The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
|
||||
and a modded targeting computer determines the best position for the user to push. \
|
||||
Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
|
||||
and counter-shock the heart, and the wearer returns to Medical a hero. Don't you even think about using it as a weapon; \
|
||||
regulations on manufacture and software locks expressly forbid it."
|
||||
icon_state = "defibrillator"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 2
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 200 // 1000 charge. Shocking, I know.
|
||||
device = /obj/item/mod_defib
|
||||
overlay_state_inactive = "module_defibrillator"
|
||||
overlay_state_active = "module_defibrillator_active"
|
||||
incompatible_modules = list(/obj/item/mod/module/defibrillator)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
|
||||
/obj/item/mod/module/defibrillator/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(device, COMSIG_DEFIB_SHOCK_APPLIED, PROC_REF(on_defib_success))
|
||||
|
||||
/obj/item/mod/module/defibrillator/proc/on_defib_success()
|
||||
SIGNAL_HANDLER // COMSIG_DEFIB_SHOCK_APPLIED
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod_defib
|
||||
name = "defibrillator gauntlets"
|
||||
desc = "A pair of paddles with flat metal surfaces that are used to deliver powerful electric shocks."
|
||||
icon = 'icons/obj/defib.dmi'
|
||||
icon_state = "defibgauntlets0" //Inhands handled by the module overlays
|
||||
force = 0
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
toolspeed = 1
|
||||
var/defib_cooldown = 5 SECONDS
|
||||
var/safety = TRUE
|
||||
/// Whether or not the paddles are on cooldown. Used for tracking icon states.
|
||||
var/on_cooldown = FALSE
|
||||
|
||||
|
||||
/obj/item/mod_defib/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/defib, cooldown = defib_cooldown, speed_multiplier = toolspeed, combat = !safety, heart_attack_chance = safety ? 0 : 100, robotic = TRUE, safe_by_default = safety, emp_proof = TRUE)
|
||||
|
||||
RegisterSignal(src, COMSIG_DEFIB_READY, PROC_REF(on_cooldown_expire))
|
||||
RegisterSignal(src, COMSIG_DEFIB_SHOCK_APPLIED, PROC_REF(after_shock))
|
||||
|
||||
/obj/item/mod_defib/proc/after_shock(obj/item/defib, mob/user)
|
||||
SIGNAL_HANDLER // COMSIG_DEFIB_SHOCK_APPLIED
|
||||
on_cooldown = TRUE
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
|
||||
/obj/item/mod_defib/proc/on_cooldown_expire(obj/item/defib)
|
||||
SIGNAL_HANDLER // COMSIG_DEFIB_READY
|
||||
on_cooldown = FALSE
|
||||
visible_message("<span class='notice'>[src] beeps: Defibrillation unit ready.</span>")
|
||||
playsound(get_turf(src), 'sound/machines/defib_ready.ogg', 50, FALSE)
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
|
||||
/obj/item/mod_defib/update_icon_state()
|
||||
icon_state = "[initial(icon_state)]"
|
||||
if(on_cooldown)
|
||||
icon_state = "[initial(icon_state)]_cooldown"
|
||||
|
||||
/obj/item/mod/module/defibrillator/combat
|
||||
name = "MOD combat defibrillator module"
|
||||
desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
|
||||
The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
|
||||
and a modded targeting computer determines the best position for the user to push. \
|
||||
Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
|
||||
and counter-shock the heart, and the wearer returns to Medical a hero. \
|
||||
Interdyne Pharmaceutics marketed the domestic version of the Healing Hands as foolproof and unusable as a weapon. \
|
||||
But when it came time to provide their operatives with usable medical equipment, they didn't hesitate to remove \
|
||||
those in-built safeties. Operatives in the field can benefit from what they dub as 'Stun Gloves', able to apply shocks \
|
||||
straight to a victims heart to disable them, or maybe even outright stop their heart with enough power."
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 400 // 2000 charge. Since you like causing heart attacks, don't you?
|
||||
module_type = MODULE_ACTIVE
|
||||
overlay_state_inactive = "module_defibrillator_combat"
|
||||
overlay_state_active = "module_defibrillator_combat_active"
|
||||
device = /obj/item/mod_defib/syndicate
|
||||
|
||||
/obj/item/mod_defib/syndicate
|
||||
name = "combat defibrillator gauntlets"
|
||||
icon_state = "syndiegauntlets0"
|
||||
safety = FALSE
|
||||
toolspeed = 2
|
||||
defib_cooldown = 2.5 SECONDS
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//Science modules for MODsuits
|
||||
|
||||
///Reagent Scanner - Lets the user scan reagents.
|
||||
/obj/item/mod/module/reagent_scanner
|
||||
name = "MOD reagent scanner module"
|
||||
desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \
|
||||
containers and projecting the information in an easy-to-read format on the wearer's display. \
|
||||
It cannot detect flavors, so that's up to you."
|
||||
icon_state = "scanner"
|
||||
module_type = MODULE_TOGGLE
|
||||
complexity = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
||||
incompatible_modules = list(/obj/item/mod/module/reagent_scanner)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
mod.helmet.scan_reagents = TRUE
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
mod.helmet.scan_reagents = FALSE
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/advanced
|
||||
name = "MOD advanced reagent scanner module"
|
||||
complexity = 0
|
||||
removable = FALSE
|
||||
var/explosion_detection_dist = 21
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/advanced/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
GLOB.doppler_arrays += src
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
GLOB.doppler_arrays -= src
|
||||
|
||||
/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(x0, y0, z0, devastation_range, heavy_impact_range,
|
||||
light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
var/turf/T = get_turf(src)
|
||||
var/dx = abs(x0 - T.x)
|
||||
var/dy = abs(y0 - T.y)
|
||||
var/distance
|
||||
if(T.z != z0)
|
||||
return
|
||||
if(dx > dy)
|
||||
distance = dx
|
||||
else
|
||||
distance = dy
|
||||
if(distance > explosion_detection_dist)
|
||||
return
|
||||
to_chat(mod.wearer, "<span class='notice'>Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]</span>")
|
||||
|
||||
///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_CHARGE_DRAIN * 5
|
||||
cooldown_time = 5 SECONDS
|
||||
accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace)
|
||||
/// Time it takes to teleport
|
||||
var/teleport_time = 1.25 SECONDS //This is a bluespace core this should be fast, like you can get a phazon with this man, we don't have anomaly refining either
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(!istype(target_turf) || target_turf.density || !(target_turf in view(9, mod.wearer))) //No. No camera bug shenanigins.
|
||||
return
|
||||
var/matrix/pre_matrix = matrix()
|
||||
pre_matrix.Scale(4, 0.25)
|
||||
var/matrix/post_matrix = matrix()
|
||||
post_matrix.Scale(0.25, 4)
|
||||
animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
|
||||
if(!do_after(mod.wearer, teleport_time, target = mod.wearer))
|
||||
animate(mod.wearer, teleport_time * 0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
|
||||
return
|
||||
animate(mod.wearer, teleport_time * 0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
|
||||
if(!do_teleport(mod.wearer, target_turf, sound_in = 'sound/effects/phasein.ogg'))
|
||||
return
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/anomaly_locked/teleporter/prebuilt
|
||||
prebuilt = TRUE
|
||||
@@ -0,0 +1,168 @@
|
||||
//Security modules for MODsuits
|
||||
|
||||
///Holster - Instantly holsters any not huge gun.
|
||||
/obj/item/mod/module/holster
|
||||
name = "MOD holster module"
|
||||
desc = "Based off typical storage compartments, this system allows the suit to holster a \
|
||||
standard firearm across its surface and allow for extremely quick retrieval. \
|
||||
While some users prefer the chest, others the forearm for quick deployment, \
|
||||
some law enforcement prefer the holster to extend from the thigh."
|
||||
icon_state = "holster"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 2
|
||||
incompatible_modules = list(/obj/item/mod/module/holster)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
allow_flags = MODULE_ALLOW_INACTIVE
|
||||
/// Gun we have holstered.
|
||||
var/obj/item/gun/holstered
|
||||
|
||||
/obj/item/mod/module/holster/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/msg = "[holstered]"
|
||||
if(!holstered)
|
||||
var/obj/item/gun/holding = mod.wearer.get_active_hand()
|
||||
if(!holding)
|
||||
to_chat(mod.wearer, "<span class='warning'>Nothing to holster!</span>")
|
||||
return
|
||||
if(!istype(holding) || holding.w_class > WEIGHT_CLASS_NORMAL) //god no holstering a BSG / combat shotgun
|
||||
to_chat(mod.wearer, "<span class='warning'>It's too big to fit!</span>")
|
||||
return
|
||||
holstered = holding
|
||||
mod.wearer.visible_message("<span class='notice'>[mod.wearer] holsters [holstered].</span>", "<span class='notice'>You holster [holstered].</span>")
|
||||
mod.wearer.unEquip(mod.wearer.get_active_hand())
|
||||
holstered.forceMove(src)
|
||||
else if(mod.wearer.put_in_active_hand(holstered))
|
||||
mod.wearer.visible_message("<span class='warning'>[mod.wearer] draws [msg], ready to shoot!</span>", \
|
||||
"<span class='warning'>You draw [msg], ready to shoot!</span>")
|
||||
else
|
||||
to_chat(mod.wearer, "<span class='warning'>You need an empty hand to draw [holstered]!</span>")
|
||||
|
||||
/obj/item/mod/module/holster/on_uninstall(deleting = FALSE)
|
||||
if(holstered)
|
||||
holstered.forceMove(drop_location())
|
||||
|
||||
/obj/item/mod/module/holster/Exited(atom/movable/gone, direction)
|
||||
. = ..()
|
||||
if(gone == holstered)
|
||||
holstered = null
|
||||
|
||||
/obj/item/mod/module/holster/Destroy()
|
||||
QDEL_NULL(holstered)
|
||||
return ..()
|
||||
|
||||
///Mirage grenade dispenser - Dispenses grenades that copy the user's appearance.
|
||||
/obj/item/mod/module/dispenser/mirage
|
||||
name = "MOD mirage grenade dispenser module"
|
||||
desc = "This module can create mirage grenades at the user's liking. These grenades create holographic copies of the user."
|
||||
icon_state = "mirage_grenade"
|
||||
cooldown_time = 20 SECONDS
|
||||
overlay_state_inactive = "module_mirage_grenade"
|
||||
dispense_type = /obj/item/grenade/mirage
|
||||
|
||||
/obj/item/mod/module/dispenser/mirage/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/obj/item/grenade/mirage/grenade = .
|
||||
grenade.attack_self(mod.wearer)
|
||||
|
||||
/obj/item/grenade/mirage
|
||||
name = "mirage grenade"
|
||||
desc = "A special device that, when activated, produces a holographic copy of the user."
|
||||
icon_state = "mirage"
|
||||
det_time = 3 SECONDS
|
||||
/// Mob that threw the grenade.
|
||||
var/mob/living/thrower
|
||||
|
||||
|
||||
/obj/item/grenade/mirage/Destroy()
|
||||
thrower = null
|
||||
return ..()
|
||||
|
||||
/obj/item/grenade/mirage/attack_self(mob/user)
|
||||
. = ..()
|
||||
thrower = user
|
||||
|
||||
/obj/item/grenade/mirage/prime()
|
||||
do_sparks(rand(3, 6), FALSE, src)
|
||||
if(thrower)
|
||||
var/mob/living/simple_animal/hostile/illusion/mirage/M = new(get_turf(src))
|
||||
M.Copy_Parent(thrower, 15 SECONDS)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/illusion/mirage //It's just standing there, menacingly
|
||||
AIStatus = AI_OFF
|
||||
density = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/illusion/mirage/death(gibbed)
|
||||
do_sparks(rand(3, 6), FALSE, src)
|
||||
return ..()
|
||||
|
||||
|
||||
///Active Sonar - Displays a hud circle on the turf of any living creatures in the given radius
|
||||
/obj/item/mod/module/active_sonar
|
||||
name = "MOD active sonar"
|
||||
desc = "Ancient tech from the 20th century, this module uses sonic waves to detect living creatures within the user's radius. \
|
||||
Its loud ping is much harder to hide in an indoor station than in the outdoor operations it was designed for."
|
||||
icon_state = "active_sonar"
|
||||
module_type = MODULE_USABLE
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 4
|
||||
complexity = 2
|
||||
incompatible_modules = list(/obj/item/mod/module/active_sonar)
|
||||
cooldown_time = 7.5 SECONDS //come on man this is discount thermals, it doesnt need a 15 second cooldown
|
||||
|
||||
/obj/item/mod/module/active_sonar/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
playsound(mod.wearer, 'sound/mecha/skyfall_power_up.ogg', vol = 20, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
|
||||
if(!do_after(mod.wearer, 1.1 SECONDS, target = mod.wearer))
|
||||
return
|
||||
var/creatures_detected = 0
|
||||
for(var/mob/living/creature in range(9, mod.wearer))
|
||||
if(creature == mod.wearer || creature.stat == DEAD)
|
||||
continue
|
||||
new /obj/effect/temp_visual/sonar_ping(mod.wearer.loc, mod.wearer, creature)
|
||||
creatures_detected++
|
||||
playsound(mod.wearer, 'sound/effects/ping_hit.ogg', vol = 75, vary = TRUE, extrarange = 9) // Should be audible for the radius of the sonar
|
||||
to_chat(mod.wearer, ("<span class='notice'>You slam your fist into the ground, sending out a sonic wave that detects [creatures_detected] living beings nearby!</span>"))
|
||||
|
||||
/obj/effect/temp_visual/sonar_ping
|
||||
duration = 3 SECONDS
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
anchored = TRUE
|
||||
randomdir = FALSE
|
||||
/// The image shown to modsuit users
|
||||
var/image/modsuit_image
|
||||
/// The person in the modsuit at the moment, really just used to remove this from their screen
|
||||
var/source_UID
|
||||
/// The icon state applied to the image created for this ping.
|
||||
var/real_icon_state = "sonar_ping"
|
||||
|
||||
/obj/effect/temp_visual/sonar_ping/Initialize(mapload, mob/living/looker, mob/living/creature)
|
||||
. = ..()
|
||||
if(!looker || !creature)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
modsuit_image = image(icon = icon, loc = src, icon_state = real_icon_state, layer = ABOVE_ALL_MOB_LAYER, pixel_x = ((creature.x - looker.x) * 32), pixel_y = ((creature.y - looker.y) * 32))
|
||||
modsuit_image.plane = ABOVE_LIGHTING_PLANE
|
||||
modsuit_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
source_UID = looker.UID()
|
||||
add_mind(looker)
|
||||
|
||||
/obj/effect/temp_visual/sonar_ping/Destroy()
|
||||
var/mob/living/previous_user = locateUID(source_UID)
|
||||
if(previous_user)
|
||||
remove_mind(previous_user)
|
||||
// Null so we don't shit the bed when we delete
|
||||
modsuit_image = null
|
||||
return ..()
|
||||
|
||||
/// Add the image to the modsuit wearer's screen
|
||||
/obj/effect/temp_visual/sonar_ping/proc/add_mind(mob/living/looker)
|
||||
looker?.client?.images |= modsuit_image
|
||||
|
||||
/// Remove the image from the modsuit wearer's screen
|
||||
/obj/effect/temp_visual/sonar_ping/proc/remove_mind(mob/living/looker)
|
||||
looker?.client?.images -= modsuit_image
|
||||
@@ -0,0 +1,42 @@
|
||||
//Service modules for MODsuits
|
||||
|
||||
///Bike Horn - Plays a bike horn sound.
|
||||
/obj/item/mod/module/bikehorn
|
||||
name = "MOD bike horn module"
|
||||
desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \
|
||||
precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound."
|
||||
icon_state = "bikehorn"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN
|
||||
incompatible_modules = list(/obj/item/mod/module/bikehorn)
|
||||
cooldown_time = 1 SECONDS
|
||||
|
||||
/obj/item/mod/module/bikehorn/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
//Waddle - Makes you waddle and squeak.
|
||||
/obj/item/mod/module/waddle
|
||||
name = "MOD waddle module"
|
||||
desc = "Some of the most primitive technology in use by Honk Co. This module works off an automatic intention system, \
|
||||
utilizing its' sensitivity to the pilot's often-limited brainwaves to directly read their next step, \
|
||||
affecting the boots they're installed in. Employing a twin-linked gravitonic drive to create \
|
||||
miniaturized etheric blasts of space-time beneath the user's feet, this enables them to... \
|
||||
to waddle around, bouncing to and fro with a pep in their step."
|
||||
icon_state = "waddle"
|
||||
complexity = 1
|
||||
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
||||
incompatible_modules = list(/obj/item/mod/module/waddle)
|
||||
|
||||
/obj/item/mod/module/waddle/on_suit_activation()
|
||||
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)
|
||||
|
||||
/obj/item/mod/module/waddle/on_suit_deactivation(deleting = FALSE)
|
||||
if(!deleting)
|
||||
qdel(mod.boots.GetComponent(/datum/component/squeak))
|
||||
mod.wearer.RemoveElement(/datum/element/waddling)
|
||||
@@ -0,0 +1,541 @@
|
||||
//Supply modules for MODsuits
|
||||
|
||||
///Internal GPS - Extends a GPS you can use.
|
||||
/obj/item/mod/module/gps
|
||||
name = "MOD internal GPS module"
|
||||
desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \
|
||||
down to the exact coordinates. This information is fed to a central database viewable from the device itself, \
|
||||
though using it to help people is up to you."
|
||||
icon_state = "gps"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
||||
incompatible_modules = list(/obj/item/mod/module/gps)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
device = /obj/item/gps/mod
|
||||
|
||||
///Hydraulic Clamp - Lets you pick up and drop crates.
|
||||
/obj/item/mod/module/clamp
|
||||
name = "MOD hydraulic clamp module"
|
||||
desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \
|
||||
However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \
|
||||
A lot of people would say that loading cargo is a dull job, but you could not disagree more."
|
||||
icon_state = "clamp"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 3
|
||||
use_power_cost = DEFAULT_CHARGE_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"
|
||||
/// Time it takes to load a crate.
|
||||
var/load_time = 3 SECONDS
|
||||
/// The max amount of crates you can carry.
|
||||
var/max_crates = 3
|
||||
/// The crates stored in the module.
|
||||
var/list/stored_crates = list()
|
||||
|
||||
/obj/item/mod/module/clamp/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!mod.wearer.Adjacent(target))
|
||||
return
|
||||
if(istype(target, /obj/structure/closet/crate))
|
||||
var/obj/structure/closet/crate/picked_crate = target
|
||||
if(!check_crate_pickup(picked_crate))
|
||||
return
|
||||
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
|
||||
if(!do_after(mod.wearer, load_time, target = target))
|
||||
return
|
||||
if(!check_crate_pickup(picked_crate))
|
||||
return
|
||||
stored_crates += picked_crate
|
||||
picked_crate.forceMove(src)
|
||||
drain_power(use_power_cost)
|
||||
else if(length(stored_crates))
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(target_turf.density)
|
||||
return
|
||||
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
|
||||
if(!do_after(mod.wearer, load_time, target = target))
|
||||
return
|
||||
if(target_turf.density)
|
||||
return
|
||||
var/obj/structure/closet/crate/dropped_crate = pop(stored_crates)
|
||||
dropped_crate.forceMove(target_turf)
|
||||
drain_power(use_power_cost)
|
||||
else
|
||||
to_chat(mod.wearer, "<span class='warning'>Invalid target!</span>")
|
||||
|
||||
/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE)
|
||||
if(deleting)
|
||||
return
|
||||
for(var/obj/structure/closet/crate/crate as anything in stored_crates)
|
||||
crate.forceMove(drop_location())
|
||||
stored_crates -= crate
|
||||
|
||||
/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target)
|
||||
if(length(stored_crates) >= max_crates)
|
||||
to_chat(mod.wearer, "<span class='warning'>Too many crates!</span>")
|
||||
return FALSE
|
||||
for(var/mob/living/mob in target.client_mobs_in_contents)
|
||||
if(mob.mob_size < MOB_SIZE_HUMAN)
|
||||
continue
|
||||
to_chat(mod.wearer, "<span class='warning'>Too heavy!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/clamp/loader
|
||||
name = "MOD loader hydraulic clamp module"
|
||||
icon_state = "clamp_loader"
|
||||
complexity = 0
|
||||
removable = FALSE
|
||||
overlay_state_inactive = null
|
||||
overlay_state_active = "module_clamp_loader"
|
||||
load_time = 1 SECONDS
|
||||
max_crates = 5
|
||||
use_mod_colors = TRUE
|
||||
|
||||
///Drill - Lets you dig through rock and basalt.
|
||||
/obj/item/mod/module/drill
|
||||
name = "MOD drill module"
|
||||
desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
|
||||
your drill is surely the one that both pierces and creates the heavens."
|
||||
icon_state = "drill"
|
||||
module_type = MODULE_ACTIVE
|
||||
complexity = 2
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN
|
||||
incompatible_modules = list(/obj/item/mod/module/drill)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
overlay_state_active = "module_drill"
|
||||
|
||||
/obj/item/mod/module/drill/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine))
|
||||
|
||||
/obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
|
||||
|
||||
/obj/item/mod/module/drill/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!mod.wearer.Adjacent(target))
|
||||
return
|
||||
if(ismineralturf(target))
|
||||
var/turf/simulated/mineral/mineral_turf = target
|
||||
mineral_turf.gets_drilled(mod.wearer)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
|
||||
SIGNAL_HANDLER
|
||||
if(!ismineralturf(bumped_into) || !drain_power(use_power_cost))
|
||||
return
|
||||
var/turf/simulated/mineral/mineral_turf = bumped_into
|
||||
mineral_turf.gets_drilled(mod.wearer)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
///Ore Bag - Lets you pick up ores and drop them from the suit.
|
||||
/obj/item/mod/module/orebag
|
||||
name = "MOD ore bag module"
|
||||
desc = "An integrated ore storage system installed into the suit, \
|
||||
this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
|
||||
It's recommended by Cybersun Industries to actually deposit that ore at local refineries."
|
||||
icon_state = "ore"
|
||||
module_type = MODULE_USABLE
|
||||
complexity = 1
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
||||
incompatible_modules = list(/obj/item/mod/module/orebag)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
allow_flags = MODULE_ALLOW_INACTIVE
|
||||
|
||||
/obj/item/mod/module/orebag/on_equip()
|
||||
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(ore_pickup))
|
||||
..()
|
||||
|
||||
/obj/item/mod/module/orebag/on_unequip()
|
||||
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
|
||||
..()
|
||||
|
||||
/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
|
||||
INVOKE_ASYNC(src, PROC_REF(move_ore), ore)
|
||||
|
||||
/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
|
||||
ore.forceMove(src)
|
||||
|
||||
/obj/item/mod/module/orebag/on_use()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
for(var/obj/item/ore as anything in contents)
|
||||
ore.forceMove(drop_location())
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/hydraulic
|
||||
name = "MOD loader hydraulic arms module"
|
||||
desc = "A pair of powerful hydraulic arms installed in a MODsuit."
|
||||
icon_state = "launch_loader"
|
||||
module_type = MODULE_ACTIVE
|
||||
removable = FALSE
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN*10
|
||||
incompatible_modules = list(/obj/item/mod/module/hydraulic)
|
||||
cooldown_time = 4 SECONDS
|
||||
overlay_state_inactive = "module_hydraulic"
|
||||
overlay_state_active = "module_hydraulic_active"
|
||||
use_mod_colors = TRUE
|
||||
/// Time it takes to launch
|
||||
var/launch_time = 2 SECONDS
|
||||
/// The overlay used to show that you are charging.
|
||||
var/image/charge_up_overlay
|
||||
|
||||
/obj/item/mod/module/hydraulic/Initialize(mapload)
|
||||
. = ..()
|
||||
charge_up_overlay = image(icon = 'icons/effects/effects.dmi', icon_state = "electricity3", layer = EFFECTS_LAYER)
|
||||
|
||||
/obj/item/mod/module/hydraulic/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(mod.wearer.buckled)
|
||||
return
|
||||
var/current_time = world.time
|
||||
var/atom/movable/plane_master_controller/pm_controller = mod.wearer.hud_used.plane_master_controllers[PLANE_MASTERS_GAME]
|
||||
for(var/key in pm_controller.controlled_planes)
|
||||
animate(pm_controller.controlled_planes[key], launch_time, transform = matrix(1.25, MATRIX_SCALE))
|
||||
mod.wearer.visible_message("<span class='warning'>[mod.wearer] starts whirring!</span>")
|
||||
playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
|
||||
mod.wearer.add_overlay(charge_up_overlay)
|
||||
var/power = launch_time
|
||||
if(!do_after(mod.wearer, launch_time, target = mod.wearer))
|
||||
power = world.time - current_time
|
||||
drain_power(use_power_cost)
|
||||
for(var/key in pm_controller.controlled_planes)
|
||||
animate(pm_controller.controlled_planes[key], 0.1 SECONDS, transform = matrix(1, MATRIX_SCALE))
|
||||
playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
|
||||
var/angle = get_angle(mod.wearer, target)
|
||||
mod.wearer.transform = mod.wearer.transform.Turn(mod.wearer.transform, angle)
|
||||
mod.wearer.throw_at(get_ranged_target_turf_direct(mod.wearer, target, power), \
|
||||
range = power, speed = max(round(0.2*power), 1), thrower = mod.wearer, spin = FALSE, \
|
||||
callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle))
|
||||
|
||||
/obj/item/mod/module/hydraulic/proc/on_throw_end(mob/user, angle)
|
||||
if(!user)
|
||||
return
|
||||
user.transform = user.transform.Turn(user.transform, angle)
|
||||
user.cut_overlay(charge_up_overlay)
|
||||
|
||||
/obj/item/mod/module/magnet
|
||||
name = "MOD loader hydraulic magnet module"
|
||||
desc = "A powerful hydraulic electromagnet able to launch crates and lockers towards the user, and keep them attached."
|
||||
icon_state = "magnet_loader"
|
||||
module_type = MODULE_ACTIVE
|
||||
removable = FALSE
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 3
|
||||
incompatible_modules = list(/obj/item/mod/module/magnet)
|
||||
cooldown_time = 1.5 SECONDS
|
||||
overlay_state_active = "module_magnet"
|
||||
use_mod_colors = TRUE
|
||||
|
||||
/obj/item/mod/module/magnet/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(istype(mod.wearer.pulling, /obj/structure/closet))
|
||||
var/obj/structure/closet/locker = mod.wearer.pulling
|
||||
playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
|
||||
locker.forceMove(mod.wearer.loc)
|
||||
locker.throw_at(target, range = 7, speed = 4, thrower = mod.wearer)
|
||||
return
|
||||
if(!istype(target, /obj/structure/closet) || !(target in view(mod.wearer)))
|
||||
to_chat(mod.wearer, "<span class='warning'>Invalid target!</span>")
|
||||
return
|
||||
var/obj/structure/closet/locker = target
|
||||
if(locker.anchored || locker.move_resist >= MOVE_FORCE_OVERPOWERING)
|
||||
return
|
||||
playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
|
||||
locker.throw_at(get_step_towards(mod.wearer, target), range = 7, speed = 3, force = MOVE_FORCE_WEAK, \
|
||||
callback = CALLBACK(src, PROC_REF(check_locker), locker))
|
||||
|
||||
/obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(istype(mod.wearer.pulling, /obj/structure/closet))
|
||||
mod.wearer.stop_pulling()
|
||||
|
||||
/obj/item/mod/module/magnet/proc/check_locker(obj/structure/closet/locker)
|
||||
if(!mod?.wearer)
|
||||
return
|
||||
if(!locker.Adjacent(mod.wearer) || !isturf(locker.loc) || !isturf(mod.wearer.loc))
|
||||
return
|
||||
mod.wearer.start_pulling(locker)
|
||||
|
||||
/obj/item/mod/module/ash_accretion
|
||||
name = "MOD ash accretion module"
|
||||
desc = "A module that collects ash from the terrain, covering the suit in a protective layer, this layer is \
|
||||
lost when moving across standard terrain."
|
||||
icon_state = "ash_accretion"
|
||||
removable = FALSE
|
||||
incompatible_modules = list(/obj/item/mod/module/ash_accretion)
|
||||
overlay_state_inactive = "module_ash"
|
||||
use_mod_colors = TRUE
|
||||
/// How many tiles we can travel to max out the armor.
|
||||
var/max_traveled_tiles = 10
|
||||
/// How many tiles we traveled through.
|
||||
var/traveled_tiles = 0
|
||||
/// Armor values per tile.
|
||||
var/armor_mod_1 = /obj/item/mod/armor/mod_ash_accretion
|
||||
/// the actual armor object
|
||||
var/obj/item/mod/armor/armor_mod_2 = null
|
||||
/// Speed added when you're fully covered in ash.
|
||||
var/speed_added = 0.5
|
||||
/// Speed that we actually added.
|
||||
var/actual_speed_added = 0
|
||||
/// Turfs that let us accrete ash.
|
||||
var/static/list/accretion_turfs
|
||||
/// Turfs that let us keep ash.
|
||||
var/static/list/keep_turfs
|
||||
|
||||
/obj/item/mod/module/ash_accretion/Initialize(mapload)
|
||||
. = ..()
|
||||
armor_mod_2 = new armor_mod_1
|
||||
|
||||
/obj/item/mod/module/ash_accretion/Destroy()
|
||||
QDEL_NULL(armor_mod_2)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/armor/mod_ash_accretion
|
||||
armor = list(MELEE = 4, BULLET = 1, LASER = 2, ENERGY = 1, BOMB = 4, RAD = 0, FIRE = 0, ACID = 0)
|
||||
|
||||
/obj/item/mod/module/ash_accretion/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!accretion_turfs)
|
||||
accretion_turfs = typecacheof(list(
|
||||
/turf/simulated/floor/plating/asteroid
|
||||
))
|
||||
if(!keep_turfs)
|
||||
keep_turfs = typecacheof(list(
|
||||
/turf/simulated/floor/plating/lava,
|
||||
/turf/simulated/floor/indestructible/hierophant
|
||||
))
|
||||
|
||||
/obj/item/mod/module/ash_accretion/on_suit_activation()
|
||||
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
||||
|
||||
/obj/item/mod/module/ash_accretion/on_suit_deactivation(deleting = FALSE)
|
||||
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
|
||||
if(!traveled_tiles)
|
||||
return
|
||||
var/list/parts = mod.mod_parts + mod
|
||||
var/speed_up = FALSE
|
||||
if(traveled_tiles == max_traveled_tiles)
|
||||
speed_up = TRUE
|
||||
for(var/obj/item/part as anything in parts)
|
||||
part.armor = part.armor.detachArmor(part.armor)
|
||||
var/obj/item/mod/armor/mod_theme_mining/A = new(src)
|
||||
part.armor = part.armor.attachArmor(A.armor) //TODO: ANYTHING BUT FUCKING THIS
|
||||
if(speed_up)
|
||||
part.slowdown += speed_added / 5
|
||||
qdel(A)
|
||||
traveled_tiles = 0
|
||||
mod.wearer.weather_immunities -= "ash"
|
||||
|
||||
/obj/item/mod/module/ash_accretion/generate_worn_overlay(user, mutable_appearance/standing)
|
||||
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/ash_accretion/proc/on_move(atom/source, atom/oldloc, dir, forced)
|
||||
if(!isturf(mod.wearer.loc)) //dont lose ash from going in a locker
|
||||
return
|
||||
if(traveled_tiles) //leave ash every tile
|
||||
new /obj/effect/temp_visual/light_ash(get_turf(src))
|
||||
if(is_type_in_typecache(mod.wearer.loc, accretion_turfs))
|
||||
if(traveled_tiles >= max_traveled_tiles)
|
||||
return
|
||||
traveled_tiles++
|
||||
var/list/parts = mod.mod_parts + mod
|
||||
var/speed_up = FALSE
|
||||
if(traveled_tiles >= max_traveled_tiles)
|
||||
to_chat(mod.wearer, "<span class='notice'>You are fully covered in ash!</span>")
|
||||
mod.wearer.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light
|
||||
animate(mod.wearer, 1 SECONDS, color = null, flags = ANIMATION_PARALLEL)
|
||||
playsound(src, 'sound/effects/sparks1.ogg', 100, TRUE)
|
||||
actual_speed_added = max(0, min(mod.slowdown_active, speed_added / 5))
|
||||
mod.wearer.weather_immunities |= "ash"
|
||||
speed_up = TRUE
|
||||
for(var/obj/item/part as anything in parts)
|
||||
part.armor = part.armor.attachArmor(armor_mod_2.armor)
|
||||
if(speed_up)
|
||||
part.slowdown -= speed_added / 5
|
||||
else if(is_type_in_typecache(mod.wearer.loc, keep_turfs))
|
||||
return
|
||||
else
|
||||
if(traveled_tiles <= 0)
|
||||
return
|
||||
var/speed_up = FALSE
|
||||
if(traveled_tiles == max_traveled_tiles)
|
||||
speed_up = TRUE
|
||||
traveled_tiles--
|
||||
var/list/parts = mod.mod_parts + mod
|
||||
for(var/obj/item/part as anything in parts)
|
||||
part.armor = part.armor.detachArmor(armor_mod_2.armor)
|
||||
if(speed_up)
|
||||
part.slowdown += actual_speed_added
|
||||
if(traveled_tiles <= 0)
|
||||
to_chat(mod.wearer, "<span class='warning'>You have ran out of ash!</span>")
|
||||
mod.wearer.weather_immunities -= "ash"
|
||||
|
||||
/obj/effect/temp_visual/light_ash
|
||||
icon_state = "light_ash"
|
||||
icon = 'icons/effects/weather_effects.dmi'
|
||||
duration = 3.2 SECONDS
|
||||
|
||||
|
||||
/obj/item/mod/module/sphere_transform
|
||||
name = "MOD sphere transform module"
|
||||
desc = "A module able to move the suit's parts around, turning it and the user into a sphere. \
|
||||
The sphere can move quickly, and launch mining bombs to decimate terrain."
|
||||
icon_state = "sphere"
|
||||
module_type = MODULE_ACTIVE
|
||||
removable = FALSE
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
|
||||
use_power_cost = DEFAULT_CHARGE_DRAIN * 3
|
||||
incompatible_modules = list(/obj/item/mod/module/sphere_transform)
|
||||
cooldown_time = 2 SECONDS
|
||||
allow_flags = MODULE_ALLOW_INCAPACITATED //Required so hands blocked doesnt block bombs
|
||||
/// Time it takes us to complete the animation.
|
||||
var/animate_time = 0.25 SECONDS
|
||||
|
||||
/obj/item/mod/module/sphere_transform/on_activation()
|
||||
if(!has_gravity(get_turf(src)))
|
||||
to_chat(mod.wearer, "<span class='warning'>ERROR, NO GRAVITY!</span>")
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE)
|
||||
mod.wearer.add_filter("mod_ball", 1, alpha_mask_filter(icon = icon('icons/mob/clothing/modsuit/mod_modules.dmi', "ball_mask"), flags = MASK_INVERSE))
|
||||
mod.wearer.add_filter("mod_blur", 2, angular_blur_filter(size = 15))
|
||||
mod.wearer.add_filter("mod_outline", 3, outline_filter(color = "#000000AA"))
|
||||
animate(mod.wearer, animate_time, pixel_y = mod.wearer.pixel_y - 4, flags = ANIMATION_PARALLEL)
|
||||
mod.wearer.SpinAnimation(1.5)
|
||||
ADD_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, "metriod[UID()]")
|
||||
ADD_TRAIT(mod.wearer, TRAIT_GOTTAGOFAST, "metroid[UID()]")
|
||||
RegisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE, PROC_REF(on_statchange))
|
||||
|
||||
/obj/item/mod/module/sphere_transform/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!deleting)
|
||||
playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1)
|
||||
animate(mod.wearer, animate_time, pixel_y = 0)
|
||||
addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, remove_filter), list("mod_ball", "mod_blur", "mod_outline")), animate_time)
|
||||
REMOVE_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, "metriod[UID()]")
|
||||
REMOVE_TRAIT(mod.wearer, TRAIT_GOTTAGOFAST, "metroid[UID()]")
|
||||
UnregisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
/obj/item/mod/module/sphere_transform/on_use()
|
||||
if(!lavaland_equipment_pressure_check(get_turf(src)))
|
||||
to_chat(mod.wearer, "<span class='warning'>ERROR, OVER PRESSURE!</span>")
|
||||
playsound(src, 'sound/weapons/gun_interactions/dry_fire.ogg', 25, TRUE)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/sphere_transform/on_select_use(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/obj/item/projectile/bomb = new /obj/item/projectile/bullet/reusable/mining_bomb(get_turf(mod.wearer))
|
||||
bomb.original = target
|
||||
bomb.firer = mod.wearer
|
||||
bomb.preparePixelProjectile(target, get_turf(target), mod.wearer)
|
||||
bomb.fire()
|
||||
playsound(src, 'sound/weapons/grenadelaunch.ogg', 75, TRUE)
|
||||
drain_power(use_power_cost)
|
||||
|
||||
/obj/item/mod/module/sphere_transform/on_active_process()
|
||||
animate(mod.wearer) //stop the animation
|
||||
mod.wearer.SpinAnimation(1.5) //start it back again
|
||||
if(!has_gravity(get_turf(src)))
|
||||
on_deactivation() //deactivate in no grav
|
||||
|
||||
/obj/item/mod/module/sphere_transform/proc/on_statchange(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
if(!mod.wearer.stat)
|
||||
return
|
||||
on_deactivation()
|
||||
|
||||
/obj/item/projectile/bullet/reusable/mining_bomb
|
||||
name = "mining bomb"
|
||||
desc = "A bomb. Why are you staring at this?"
|
||||
icon_state = "mine_bomb"
|
||||
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
||||
damage = 0
|
||||
range = 6
|
||||
flag = "bomb"
|
||||
light_range = 1
|
||||
light_power = 1
|
||||
light_color = LIGHT_COLOR_ORANGE
|
||||
ammo_type = /obj/structure/mining_bomb
|
||||
|
||||
/obj/structure/mining_bomb
|
||||
name = "mining bomb"
|
||||
desc = "A bomb. Why are you staring at this?"
|
||||
icon_state = "mine_bomb"
|
||||
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF|LAVA_PROOF
|
||||
light_range = 1
|
||||
light_power = 1
|
||||
light_color = LIGHT_COLOR_ORANGE
|
||||
/// Time to prime the explosion
|
||||
var/prime_time = 0.5 SECONDS
|
||||
/// Time to explode from the priming
|
||||
var/explosion_time = 1 SECONDS
|
||||
/// Damage done on explosion.
|
||||
var/damage = 12
|
||||
/// Damage multiplier on hostile fauna.
|
||||
var/fauna_boost = 4
|
||||
/// Image overlaid on explosion.
|
||||
var/static/image/explosion_image
|
||||
|
||||
/obj/structure/mining_bomb/Initialize(mapload, atom/movable/firer)
|
||||
. = ..()
|
||||
generate_image()
|
||||
addtimer(CALLBACK(src, PROC_REF(prime), firer), prime_time)
|
||||
|
||||
/obj/structure/mining_bomb/proc/generate_image()
|
||||
explosion_image = image('icons/effects/96x96.dmi', "judicial_explosion")
|
||||
explosion_image.pixel_x = -32
|
||||
explosion_image.pixel_y = -32
|
||||
|
||||
/obj/structure/mining_bomb/proc/prime(atom/movable/firer)
|
||||
add_overlay(explosion_image)
|
||||
addtimer(CALLBACK(src, PROC_REF(boom), firer), explosion_time)
|
||||
|
||||
/obj/structure/mining_bomb/proc/boom(atom/movable/firer)
|
||||
visible_message("<span class='danger'>[src] explodes!</span>")
|
||||
playsound(src, 'sound/magic/magic_missile.ogg', 200, vary = TRUE)
|
||||
for(var/turf/T in circlerangeturfs(src, 2))
|
||||
if(ismineralturf(T))
|
||||
var/turf/simulated/mineral/mineral_turf = T
|
||||
mineral_turf.gets_drilled(firer)
|
||||
for(var/mob/living/mob in range(1, src))
|
||||
mob.apply_damage(damage * (ishostile(mob) ? fauna_boost : 1), BRUTE, spread_damage = TRUE)
|
||||
if(!ishostile(mob) || !firer)
|
||||
continue
|
||||
var/mob/living/simple_animal/hostile/hostile_mob = mob
|
||||
hostile_mob.GiveTarget(firer)
|
||||
for(var/obj/object in range(1, src))
|
||||
object.take_damage(damage, BRUTE, BOMB)
|
||||
qdel(src)
|
||||
@@ -0,0 +1,93 @@
|
||||
//Visor modules for MODsuits
|
||||
|
||||
///Base Visor - Adds a specific HUD and traits to you.
|
||||
/obj/item/mod/module/visor
|
||||
name = "MOD visor module"
|
||||
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 = 1
|
||||
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
||||
incompatible_modules = list(/obj/item/mod/module/visor)
|
||||
cooldown_time = 0.5 SECONDS
|
||||
/// The HUD type given by the visor.
|
||||
var/hud_type
|
||||
/// The trait given by the visor.
|
||||
var/visor_trait = list()
|
||||
|
||||
/obj/item/mod/module/visor/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(hud_type)
|
||||
var/datum/atom_hud/hud = GLOB.huds[hud_type]
|
||||
hud.add_hud_to(mod.wearer)
|
||||
if(length(visor_trait))
|
||||
ADD_TRAIT(mod.wearer, visor_trait, MODSUIT_TRAIT)
|
||||
mod.wearer.update_sight()
|
||||
|
||||
/obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(hud_type)
|
||||
var/datum/atom_hud/hud = GLOB.huds[hud_type]
|
||||
hud.remove_hud_from(mod.wearer)
|
||||
if(length(visor_trait))
|
||||
REMOVE_TRAIT(mod.wearer, visor_trait, MODSUIT_TRAIT)
|
||||
mod.wearer.update_sight()
|
||||
|
||||
//Medical Visor - Gives you a medical HUD.
|
||||
/obj/item/mod/module/visor/medhud
|
||||
name = "MOD medical visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \
|
||||
biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \
|
||||
access data such as patient files in a convenient readout. They say these also let you see behind you."
|
||||
icon_state = "medhud_visor"
|
||||
hud_type = DATA_HUD_MEDICAL_ADVANCED
|
||||
|
||||
//Diagnostic Visor - Gives you a diagnostic HUD.
|
||||
/obj/item/mod/module/visor/diaghud
|
||||
name = "MOD diagnostic visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \
|
||||
from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \
|
||||
and integrity of such. They say these also let you see behind you."
|
||||
icon_state = "diaghud_visor"
|
||||
hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED
|
||||
|
||||
//Security Visor - Gives you a security HUD.
|
||||
/obj/item/mod/module/visor/sechud
|
||||
name = "MOD security visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \
|
||||
plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \
|
||||
and generally know who to shoot. They say these also let you see behind you."
|
||||
icon_state = "sechud_visor"
|
||||
hud_type = DATA_HUD_SECURITY_ADVANCED
|
||||
|
||||
//Meson Visor - Gives you meson vision.
|
||||
/obj/item/mod/module/visor/meson
|
||||
name = "MOD meson visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \
|
||||
technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \
|
||||
through walls, regardless of lighting conditions. They say these also let you see behind you."
|
||||
icon_state = "meson_visor"
|
||||
visor_trait = TRAIT_MESON_VISION
|
||||
|
||||
//Thermal Visor - Gives you thermal vision.
|
||||
/obj/item/mod/module/visor/thermal
|
||||
name = "MOD thermal visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \
|
||||
the thermal radiation output of objects near the user. While it can detect the heat output of even something as \
|
||||
small as a rodent, it still produces irritating red overlay. They say these also let you see behind you."
|
||||
icon_state = "thermal_visor"
|
||||
origin_tech = "combat=6;engineering=6;syndicate=2"
|
||||
visor_trait = TRAIT_THERMAL_VISION
|
||||
|
||||
//Night Visor - Gives you night vision.
|
||||
/obj/item/mod/module/visor/night
|
||||
name = "MOD night visor module"
|
||||
desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \
|
||||
this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \
|
||||
yet brightening everything into a spooky green glow. They say these also let you see behind you."
|
||||
icon_state = "night_visor"
|
||||
origin_tech = "combat=5;engineering=5;syndicate=1"
|
||||
visor_trait = TRAIT_NIGHT_VISION
|
||||
Reference in New Issue
Block a user