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:
Qwertytoforty
2023-08-19 16:36:13 -04:00
committed by GitHub
parent 9108007709
commit 76a845d380
169 changed files with 9276 additions and 329 deletions
+324
View File
@@ -0,0 +1,324 @@
## Introduction
This is a step by step guide for creating a MODsuit theme, skin and module.
## Theme
This is pretty simple, we go [here](./mod_theme.dm) and add a new definition, let's go with a Psychologist theme as an example. \
Their names should be like model names or use similar adjectives, like "magnate" or simply "engineering", so we'll go with "psychological". \
After that, it's good to decide what company is manufacturing the suit, and a basic description of what it offers, we'll write that down in the desc. \
So, let's our suit should be a low-power usage with lowered module capacity. We'd go with something like this.
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
```
For people that want to see additional stuff, we add an extended description with some more insight into what the suit does. We also set the default skin to usually the theme name, like so.
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
for operating at lower power levels, keeping people sane. As consequence, the capacity \
of the suit has decreased, not being able to fit many modules at all."
default_skin = "psychological"
```
Next we want to set the statistics, you can view them all in the theme file, so let's just grab our relevant ones, armor, charge and capacity and set them to what we establilished. \
Currently crew MODsuits should be lightly armored in combat relevant stats.
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
for operating at lower power levels, keeping people sane. As consequence, the capacity \
of the suit has decreased, not being able to fit many modules at all."
default_skin = "psychological"
armor_type = /datum/armor/modtheme_psychological
complexity_max = DEFAULT_MAX_COMPLEXITY - 7
charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
```
Now we have a basic theme, it lacks a skin which will be covered in the next section, and an item, which we will add right now. \
Let's go into [here](./mod_types.dm). It's as simple as adding a new suit type with the appropriate modules you want.
```dm
/obj/item/mod/control/pre_equipped/psychological
theme = /datum/mod_theme/psychological
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/flashlight,
)
```
This will create our psychological suit, equipped with a storage and flashlight modules by default. We might also want to make it craftable, in which case we go [here](./mod_construction.dm) and set this.
```dm
/obj/item/mod/construction/armor/psychological
theme = /datum/mod_theme/psychological
```
After that we put it in the techweb or whatever other source we want. Now our suit is almost ready, it just needs a skin.
## Skin
So, now that we have our theme, we want to add a skin to it (or another theme of our choosing). Let's start with a basis.
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
for operating at lower power levels, keeping people sane. As consequence, the capacity \
of the suit has decreased, not being able to fit many modules at all."
default_skin = "psychological"
armor_type = /datum/armor/modtheme_psychological
complexity_max = DEFAULT_MAX_COMPLEXITY - 7
charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
skins = list(
"psychological" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
),
CHESTPLATE_FLAGS = list(
),
GAUNTLETS_FLAGS = list(
),
BOOTS_FLAGS = list(
),
),
)
```
We now have a psychological skin, this will apply the psychological icons to every part of the suit. Next we'll be looking at the flags. Boots, gauntlets and the chestplate are usually very standard, we set their thickmaterial and pressureproofness while hiding the jumpsuit on the chestplate. On the helmet however, we'll actually look at its' icon. \
For example, if our helmet's icon covers the full head (like the research skin), we want to do something like this.
```dm
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
```
Otherwise, with an open helmet that becomes closed (like the engineering skin), we'd do this.
```dm
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
```
There are specific cases of helmets that semi-cover the head, like the cosmohonk, apocryphal and whatnot. You can look at these for more specific suits. So let's say our suit is an open helmet design, and also add an alternate skin with a closed helmet called psychotherapeutic. It'd look something like this.
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
for operating at lower power levels, keeping people sane. As consequence, the capacity \
of the suit has decreased, not being able to fit many modules at all."
default_skin = "psychological"
armor_type = /datum/armor/modtheme_psychological
complexity_max = DEFAULT_MAX_COMPLEXITY - 7
charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
skins = list(
"psychological" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
"psychotherapeutic" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
```
Thus we finished our codeside. Now we go to the icon files for the suits and simply add our new skin's icons. \
Now our suit is finished. But let's say we want to give it an unique module.
## Module
So, for our psychological suit, let's say we want a module that heals the brain damage of everyone in range. \
As it's a medical module, we'll put it [here](modules/modules_medical.dm). Let's start with the object definition.
```dm
/obj/item/mod/module/neuron_healer
name = "MOD neuron healer module"
desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
that heal neuron damage of everyone nearby, getting their brains to a better state."
icon_state = "neuron_healer"
```
As we want this effect to be on demand, we probably want this to be an usable module. There are four types of modules:
- Passive: These have a passive effect.
- Togglable: You can turn these on and off.
- Usable: You can use these for a one time effect.
- Active: You can only have one selected at a time. It gives you a special click effect.
As we have an usable module, we want to set a cooldown time. All modules are also incompatible with themselves, have a specific power cost and complexity varying on how powerful they are, so let's update our definition, and also add a new variable for how much brain damage we'll heal.
```dm
/obj/item/mod/module/neuron_healer
name = "MOD neuron healer module"
desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
that heal neuron damage of everyone nearby, getting their brains to a better state."
icon_state = "neuron_healer"
module_type = MODULE_USABLE
complexity = 3
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/neuron_healer)
cooldown_time = 15 SECONDS
var/brain_damage_healed = 25
```
Now, we want to override the on_use proc for our new effect. We want to make sure the use checks passed from parent. You can read about most procs and variables by reading [this](modules/_module.dm)
```dm
/obj/item/mod/module/neuron_healer/on_use()
. = ..()
if(!.)
return
```
After this, we want to put our special code, a basic effect of healing all mobs nearby for their brain damage and creating a beam to them.
```dm
/obj/item/mod/module/neuron_healer/on_use()
. = ..()
if(!.)
return
for(var/mob/living/carbon/carbon_mob in range(5, src))
if(carbon_mob == mod.wearer)
continue
carbon_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -brain_damage_healed)
mod.wearer.Beam(carbon_mob, icon_state = "plasmabeam", time = 1.5 SECONDS)
playsound(src, 'sound/effects/magic.ogg', 100, TRUE)
drain_power(use_power_cost)
```
We now have a basic module, we can add it to the techwebs to make it printable ingame, and we can add an inbuilt, advanced version of it for our psychological suit. We'll give it more healing power, no complexity and make it unremovable.
```dm
/obj/item/mod/module/neuron_healer/advanced
name = "MOD advanced neuron healer module"
complexity = 0
brain_damage_healed = 50
```
Now we want to add it to the psychological theme, which is very simple, finishing with this:
```dm
/datum/mod_theme/psychological
name = "psychological"
desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
for operating at lower power levels, keeping people sane. As consequence, the capacity \
of the suit has decreased, not being able to fit many modules at all."
default_skin = "psychological"
armor_type = /datum/armor/modtheme_psychological
complexity_max = DEFAULT_MAX_COMPLEXITY - 7
charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
inbuilt_modules = list(/obj/item/mod/module/neuron_healer/advanced)
skins = list(
"psychological" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
"psychotherapeutic" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
```
## Ending
This finishes this hopefully easy to follow along tutorial. You should now know how to make a basic theme, a skin for it, and a module.
+127
View File
@@ -0,0 +1,127 @@
/datum/action/item_action/mod
background_icon_state = "bg_mod"
button_icon_state = "bg_mod_border"
icon_icon = 'icons/mob/actions/actions_mod.dmi'
button_icon = 'icons/mob/actions/actions_mod.dmi'
check_flags = AB_CHECK_CONSCIOUS
use_itemicon = FALSE
/datum/action/item_action/mod/New(Target, custom_icon, custom_icon_state)
..()
if(!ismodcontrol(Target))
qdel(src)
/datum/action/item_action/mod/Trigger(left_click, attack_self)
if(!IsAvailable())
return FALSE
var/obj/item/mod/control/mod = target
if(mod.malfunctioning && prob(75))
to_chat(usr, "<span class='warning'>The module fails to activate!</span>")
return FALSE
return TRUE
/datum/action/item_action/mod/deploy
name = "Deploy MODsuit"
desc = "LMB: Deploy/Undeploy full suit. MMB: Deploy/Undeploy part."
button_icon_state = "deploy"
/datum/action/item_action/mod/deploy/Trigger(left_click, attack_self)
. = ..()
if(!.)
return
var/obj/item/mod/control/mod = target
if(left_click)
mod.quick_deploy(usr)
else
mod.choose_deploy(usr)
/datum/action/item_action/mod/activate
name = "Activate MODsuit"
desc = "LMB: Activate/Deactivate suit with prompt. MMB: Activate/Deactivate suit skipping prompt."
button_icon_state = "activate"
/// First time clicking this will set it to TRUE, second time will activate it.
var/ready = FALSE
/datum/action/item_action/mod/activate/Trigger(left_click, attack_self)
. = ..()
if(!.)
return
if(!ready && left_click)
ready = TRUE
button_icon_state = "activate-ready"
addtimer(CALLBACK(src, PROC_REF(reset_ready)), 3 SECONDS)
return
var/obj/item/mod/control/mod = target
reset_ready()
mod.toggle_activate(usr)
/// Resets the state requiring to be doubleclicked again.
/datum/action/item_action/mod/activate/proc/reset_ready()
ready = FALSE
button_icon_state = initial(button_icon_state)
/datum/action/item_action/mod/module
name = "Toggle Module"
desc = "Toggle a MODsuit module."
button_icon_state = "module"
/datum/action/item_action/mod/module/Trigger(left_click, attack_self)
. = ..()
if(!.)
return
var/obj/item/mod/control/mod = target
mod.quick_module(usr)
/datum/action/item_action/mod/panel
name = "MODsuit Panel"
desc = "Open the MODsuit's panel."
button_icon_state = "panel"
/datum/action/item_action/mod/panel/Trigger(left_click, attack_self)
. = ..()
if(!.)
return
var/obj/item/mod/control/mod = target
mod.ui_interact(usr)
/datum/action/item_action/mod/pinned_module
desc = "Activate the module."
icon_icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
button_icon = 'icons/mob/actions/actions_mod.dmi'
button_icon_state = "module"
/// Module we are linked to.
var/obj/item/mod/module/module
/// A ref to the mob we are pinned to.
var/pinner_uid
/datum/action/item_action/mod/pinned_module/New(Target, custom_icon, custom_icon_state, obj/item/mod/module/linked_module, mob/user)
name = "Activate [capitalize(linked_module.name)]"
desc = "Quickly activate [linked_module]."
..()
module = linked_module
button_icon_state = module.icon_state
if(linked_module.allow_flags & MODULE_ALLOW_INCAPACITATED)
// clears check hands
check_flags = AB_CHECK_CONSCIOUS
Grant(user)
/datum/action/item_action/mod/pinned_module/Destroy()
UnregisterSignal(module, list(COMSIG_MODULE_ACTIVATED, COMSIG_MODULE_DEACTIVATED, COMSIG_MODULE_USED))
module.pinned_to -= pinner_uid
module = null
return ..()
/datum/action/item_action/mod/pinned_module/Grant(mob/user)
var/user_uid = UID(user)
if(!pinner_uid)
pinner_uid = user_uid
module.pinned_to[pinner_uid] = src
else if(pinner_uid != user_uid)
return
return ..()
/datum/action/item_action/mod/pinned_module/Trigger(left_click, attack_self)
. = ..()
if(!.)
return
module.on_select()
+248
View File
@@ -0,0 +1,248 @@
/// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted.
/obj/item/mod/control/proc/choose_deploy(mob/user)
if(!length(mod_parts))
return
var/list/display_names = list()
var/list/items = list()
for(var/obj/item/part as anything in mod_parts)
display_names[part.name] = part.UID()
var/image/part_image = image(icon = part.icon, icon_state = part.icon_state)
if(part.loc != src)
part_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
items += list(part.name = part_image)
var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE)
if(!pick)
return
var/part_reference = display_names[pick]
var/obj/item/part = locateUID(part_reference)
if(!istype(part) || user.incapacitated())
return
if(active || activating)
to_chat(user, "<span class='warning'>Deactivate the suit first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
var/parts_to_check = mod_parts - part
if(part.loc == src)
deploy(user, part)
on_mod_deployed(user)
for(var/obj/item/checking_part as anything in parts_to_check)
if(checking_part.loc != src)
continue
choose_deploy(user)
break
else
retract(user, part)
on_mod_retracted(user)
for(var/obj/item/checking_part as anything in parts_to_check)
if(checking_part.loc == src)
continue
choose_deploy(user)
break
/// Quickly deploys all parts (or retracts if all are on the wearer)
/obj/item/mod/control/proc/quick_deploy(mob/user)
if(active || activating)
to_chat(user, "<span class='warning'>Deactivate the suit first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
var/deploy = TRUE
for(var/obj/item/part as anything in mod_parts)
if(part.loc == src)
continue
deploy = FALSE
break
for(var/obj/item/part as anything in mod_parts)
if(deploy && part.loc == src)
deploy(null, part, TRUE)
else if(!deploy && part.loc != src)
retract(null, part, TRUE)
wearer.visible_message("<span class='notice'>[wearer]'s [src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss.</span>",
"<span class='notice'>[src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss.</span>",
"You hear a mechanical hiss.")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
if(deploy)
on_mod_deployed(user)
else
on_mod_retracted(user)
return TRUE
/// Deploys a part of the suit onto the user.
/obj/item/mod/control/proc/deploy(mob/user, obj/item/part, mass = FALSE)
if(part.loc != src)
if(!user)
return FALSE
to_chat(user, "<span class='warning'>[part.name] already deployed!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
if(part in overslotting_parts)
var/obj/item/overslot = wearer.get_item_by_slot(slot_bitfield_to_slot(part.slot_flags))
if(overslot)
wearer.unEquip(overslot, TRUE)
overslotting_parts[part] = overslot
overslot.forceMove(part)
RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit))
if(wearer.equip_to_slot_if_possible(part, slot_bitfield_to_slot(part.slot_flags), disable_warning = TRUE))
part.flags |= NODROP
if(mass)
return TRUE
wearer.visible_message("<span class='notice'>[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss.</span>",
"<span class='notice'>[part] deploy[part.p_s()] with a mechanical hiss.</span>",
"You hear a mechanical hiss.")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
else
if(!user)
return FALSE
to_chat(user, "<span class='warning'>You already have clothing there!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
/// Retract a part of the suit from the user.
/obj/item/mod/control/proc/retract(mob/user, obj/item/part, mass = FALSE)
if(part.loc == src)
if(!user)
return FALSE
to_chat(user, "<span class='warning'>You already have retracted there!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
part.flags &= ~NODROP
wearer.unEquip(part, TRUE)
part.forceMove(src)
if(overslotting_parts[part])
UnregisterSignal(part, COMSIG_ATOM_EXITED)
var/obj/item/overslot = overslotting_parts[part]
if(!wearer.equip_to_slot_if_possible(overslot, slot_bitfield_to_slot(overslot.slot_flags), disable_warning = TRUE))
overslot.forceMove(get_turf(wearer))
overslotting_parts[part] = null
if(mass)
return TRUE
wearer.visible_message("<span class='notice'>[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss.</span>",
"<span class='notice'>[part] retract[part.p_s()] back into [src] with a mechanical hiss.</span>",
"You hear a mechanical hiss.")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on
/obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE)
if(!wearer)
if(!force_deactivate)
to_chat(user, "<span class='warning'>Equip your suit first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE))
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
for(var/obj/item/part as anything in mod_parts)
if(!force_deactivate && part.loc == src)
to_chat(user, "<span class='warning'>Deploy all parts first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(locked && !active && !allowed(user) && !force_deactivate)
to_chat(user, "<span class='warning'>Insufficient access!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!get_charge() && !force_deactivate)
to_chat(user, "<span class='warning'>Suit is not powered!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(open && !force_deactivate)
to_chat(user, "<span class='warning'>Close the suit panel!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(activating)
if(!force_deactivate)
to_chat(user, "<span class='warning'>Suit is already [active ? "shutting down" : "starting up"]!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
for(var/obj/item/mod/module/module as anything in modules)
if(!module.active || (module.allow_flags & MODULE_ALLOW_INACTIVE))
continue
module.on_deactivation(display_message = FALSE)
activating = TRUE
to_chat(wearer, "<span class='notice'>MODsuit [active ? "shutting down" : "starting up"].</span>")
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
if(has_wearer())
to_chat(wearer, "<span class='notice'>[boots] [active ? "relax their grip on your legs" : "seal around your feet"].</span>")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(boots, seal = !active)
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
if(has_wearer())
to_chat(wearer, "<span class='notice'>[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"].</span>")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(gauntlets, seal = !active)
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
if(has_wearer())
to_chat(wearer, "<span class='notice'>[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"].</span>")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(chestplate, seal = !active)
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
if(has_wearer())
to_chat(wearer, "<span class='notice'>[helmet] hisses [active ? "open" : "closed"].</span>")
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(helmet, seal = !active)
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
if(has_wearer())
to_chat(wearer, "<span class='notice'>Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer].</span>")
finish_activation(on = !active)
if(active)
playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
if(!malfunctioning)
wearer.playsound_local(get_turf(src), 'sound/mecha/nominal.ogg', 50)
else
playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
activating = FALSE
SEND_SIGNAL(src, COMSIG_MOD_TOGGLED, user)
return TRUE
///Seals or unseals the given part
/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal)
if(seal)
part.icon_state = "[skin]-[part.base_icon_state]-sealed"
part.flags |= part.visor_flags
part.flags_inv |= part.visor_flags_inv
part.flags_cover |= part.visor_flags_cover
part.heat_protection = initial(part.heat_protection)
part.cold_protection = initial(part.cold_protection)
else
part.icon_state = "[skin]-[part.base_icon_state]"
part.flags_cover &= ~part.visor_flags_cover
part.flags_inv &= ~part.visor_flags_inv
part.flags &= ~part.visor_flags
part.heat_protection = NONE
part.cold_protection = NONE
if(ishuman(wearer))
var/mob/living/carbon/human/H = wearer
H.regenerate_icons()
/// Finishes the suit's activation, starts processing
/obj/item/mod/control/proc/finish_activation(on)
active = on
if(active)
for(var/obj/item/mod/module/module as anything in modules)
module.on_suit_activation()
START_PROCESSING(SSobj, src)
else
for(var/obj/item/mod/module/module as anything in modules)
module.on_suit_deactivation()
STOP_PROCESSING(SSobj, src)
update_speed()
update_icon_state()
wearer.regenerate_icons()
/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
/obj/item/mod/control/proc/quick_activation()
var/seal = TRUE
for(var/obj/item/part as anything in mod_parts)
if(!deploy(null, part))
seal = FALSE
if(!seal)
return
for(var/obj/item/part as anything in mod_parts)
seal_part(part, seal = TRUE)
finish_activation(on = TRUE)
/obj/item/mod/control/proc/has_wearer()
return wearer
/obj/item/mod/control/proc/on_mod_deployed(mob/user)
SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user)
/obj/item/mod/control/proc/on_mod_retracted(mob/user)
SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user)
+85
View File
@@ -0,0 +1,85 @@
/obj/item/clothing/head/mod
name = "MOD helmet"
desc = "A helmet for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
icon_state = "standard-helmet"
base_icon_state = "helmet"
item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
body_parts_covered = HEAD
heat_protection = HEAD
cold_protection = HEAD
sprite_sheets = list(
"Grey" = 'icons/mob/clothing/modsuit/species/grey_helmets.dmi',
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
)
/obj/item/clothing/suit/mod
name = "MOD chestplate"
desc = "A chestplate for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
icon_state = "standard-chestplate"
base_icon_state = "chestplate"
item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
blood_overlay_type = "armor"
allowed = list(
/obj/item/tank/internals,
/obj/item/flashlight,
/obj/item/tank/jetpack/oxygen/captain,
)
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
body_parts_covered = UPPER_TORSO|LOWER_TORSO
heat_protection = UPPER_TORSO|LOWER_TORSO
cold_protection = UPPER_TORSO|LOWER_TORSO
hide_tail_by_species = list("modsuit")
sprite_sheets = list(
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
)
/obj/item/clothing/gloves/mod
name = "MOD gauntlets"
desc = "A pair of gauntlets for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
icon_state = "standard-gauntlets"
base_icon_state = "gauntlets"
item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
body_parts_covered = HANDS|ARMS
heat_protection = HANDS|ARMS
cold_protection = HANDS|ARMS
sprite_sheets = list(
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
)
/obj/item/clothing/shoes/mod
name = "MOD boots"
desc = "A pair of boots for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
icon_state = "standard-boots"
base_icon_state = "boots"
item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
body_parts_covered = FEET|LEGS
heat_protection = FEET|LEGS
cold_protection = FEET|LEGS
sprite_sheets = list(
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
)
var/magbooted
/obj/item/clothing/shoes/mod/negates_gravity()
return magbooted
+281
View File
@@ -0,0 +1,281 @@
/obj/item/mod/construction
desc = "A part used in MOD construction. You could insert it into a MOD shell."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
icon_state = "rack_parts"
/obj/item/mod/construction/helmet
name = "MOD helmet"
desc = "You could insert it into a MOD shell."
icon_state = "helmet"
/obj/item/mod/construction/chestplate
name = "MOD chestplate"
icon_state = "chestplate"
/obj/item/mod/construction/gauntlets
name = "MOD gauntlets"
icon_state = "gauntlets"
/obj/item/mod/construction/boots
name = "MOD boots"
icon_state = "boots"
/obj/item/mod/construction/broken_core
name = "broken MOD core"
icon_state = "mod-core"
desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though."
/obj/item/mod/construction/broken_core/examine(mob/user)
. = ..()
. += "<span class='notice'>You could repair it with a <b>screwdriver</b>...</span>"
/obj/item/mod/construction/broken_core/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
if(!tool.use_tool(src, user, 5 SECONDS, volume = 30))
return
new /obj/item/mod/core/standard(drop_location())
qdel(src)
/obj/item/mod/construction/plating
name = "MOD external plating"
desc = "External plating used to finish a MOD control unit."
icon_state = "standard-plating"
var/datum/mod_theme/theme = /datum/mod_theme/standard
/obj/item/mod/construction/plating/Initialize(mapload)
. = ..()
var/datum/mod_theme/used_theme = GLOB.mod_themes[theme]
name = "MOD [used_theme.name] external plating"
desc = "[desc] [used_theme.desc]"
icon_state = "[used_theme.default_skin]-plating"
/obj/item/mod/construction/plating/engineering
theme = /datum/mod_theme/engineering
/obj/item/mod/construction/plating/atmospheric
theme = /datum/mod_theme/atmospheric
/obj/item/mod/construction/plating/medical
theme = /datum/mod_theme/medical
/obj/item/mod/construction/plating/security
theme = /datum/mod_theme/security
/obj/item/mod/construction/plating/cosmohonk
theme = /datum/mod_theme/cosmohonk
/obj/item/mod/construction/plating/rescue //I want to add a way to get the rarer modsuit types, that is limited. A low chance for traders to have plating for it seems interesting
theme = /datum/mod_theme/rescue
/obj/item/mod/construction/plating/safeguard //Continued from above, none of these are steal objectives, and only the CE or RD one comes pre-installed with modules. You are getting the protection / speed / looks of these hardsuits, but no special modules.
theme = /datum/mod_theme/safeguard
/obj/item/mod/construction/plating/advanced //This may be a bad idea. I think this is an interesting idea. And you still need robotics to build it, and traders can charge as much for it as they want. Also with ones like the CE modsuit, it is the flagship mod. That means it is sold a lot.
theme = /datum/mod_theme/advanced
/obj/item/mod/construction/plating/research //Don't think people will want the RD one though, it is as slow as shit. Anyway, here it is. Surely this will not end poorly.
theme = /datum/mod_theme/research
#define START_STEP "start"
#define CORE_STEP "core"
#define SCREWED_CORE_STEP "screwed_core"
#define HELMET_STEP "helmet"
#define CHESTPLATE_STEP "chestplate"
#define GAUNTLETS_STEP "gauntlets"
#define BOOTS_STEP "boots"
#define WRENCHED_ASSEMBLY_STEP "wrenched_assembly"
#define SCREWED_ASSEMBLY_STEP "screwed_assembly"
/obj/item/mod/construction/shell
name = "MOD shell"
desc = "A MOD shell."
icon_state = "mod-construction_start"
var/obj/item/core
var/obj/item/helmet
var/obj/item/chestplate
var/obj/item/gauntlets
var/obj/item/boots
var/construction_step = START_STEP
/obj/item/mod/construction/shell/examine(mob/user)
. = ..()
var/display_text
switch(construction_step)
if(START_STEP)
display_text = "It looks like it's missing a <b>MOD core</b>..."
if(CORE_STEP)
display_text = "The core seems <b>loose</b>..."
if(SCREWED_CORE_STEP)
display_text = "It looks like it's missing a <b>helmet</b>..."
if(HELMET_STEP)
display_text = "It looks like it's missing a <b>chestplate</b>..."
if(CHESTPLATE_STEP)
display_text = "It looks like it's missing <b>gauntlets</b>..."
if(GAUNTLETS_STEP)
display_text = "It looks like it's missing <b>boots</b>..."
if(BOOTS_STEP)
display_text = "The assembly seems <b>unsecured</b>..."
if(WRENCHED_ASSEMBLY_STEP)
display_text = "The assembly seems <b>loose</b>..."
if(SCREWED_ASSEMBLY_STEP)
display_text = "All it's missing is <b>external plating</b>..."
. += "<span class='notice'>[display_text]</span>"
/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, params)
. = ..()
switch(construction_step)
if(START_STEP)
if(!istype(part, /obj/item/mod/core))
return
if(!user.drop_item())
to_chat(user, "<span class='warning'>[part] is stuck to you and cannot be placed into [src].</span>")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
to_chat(user, "<span class='notice'>Core inserted.</span>")
core = part
core.forceMove(src)
construction_step = CORE_STEP
if(CORE_STEP)
if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Core screwed.</span>")
construction_step = SCREWED_CORE_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
core.forceMove(drop_location())
to_chat(user, "<span class='notice'>Core removed.</span>")
construction_step = START_STEP
if(SCREWED_CORE_STEP)
if(istype(part, /obj/item/mod/construction/helmet)) //Construct
if(!user.drop_item())
to_chat(user, "<span class='warning'>[part] is stuck to you and cannot be placed into [src].</span>")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
to_chat(user, "<span class='notice'>Helmet added.</span>")
helmet = part
helmet.forceMove(src)
construction_step = HELMET_STEP
else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Core unscrewed.</span>")
construction_step = CORE_STEP
if(HELMET_STEP)
if(istype(part, /obj/item/mod/construction/chestplate)) //Construct
if(!user.drop_item())
to_chat(user, "<span class='warning'>[part] is stuck to you and cannot be placed into [src].</span>")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
to_chat(user, "<span class='notice'>Chestplate added.</span>")
forceMove(src)
chestplate = part
chestplate.forceMove(src)
construction_step = CHESTPLATE_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
helmet.forceMove(drop_location())
to_chat(user, "<span class='notice'>Helmet removed.</span>")
helmet = null
construction_step = SCREWED_CORE_STEP
if(CHESTPLATE_STEP)
if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct
if(!user.drop_item())
to_chat(user, "<span class='warning'>[part] is stuck to you and cannot be placed into [src].</span>")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
to_chat(user, "<span class='notice'>Gauntlets added.</span>")
gauntlets = part
gauntlets.forceMove(src)
construction_step = GAUNTLETS_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
chestplate.forceMove(drop_location())
to_chat(user, "<span class='notice'>Chestplate removed.</span>")
chestplate = null
construction_step = HELMET_STEP
if(GAUNTLETS_STEP)
if(istype(part, /obj/item/mod/construction/boots)) //Construct
if(!user.drop_item())
to_chat(user, "<span class='warning'>[part] is stuck to you and cannot be placed into [src].</span>")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
to_chat(user, "<span class='notice'>Boots added.</span>")
boots = part
boots.forceMove(src)
construction_step = BOOTS_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
gauntlets.forceMove(drop_location())
to_chat(user, "<span class='notice'>Gauntlets removed.</span>")
gauntlets = null
construction_step = CHESTPLATE_STEP
if(BOOTS_STEP)
if(part.tool_behaviour == TOOL_WRENCH) //Construct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Assembly secured.</span>")
construction_step = WRENCHED_ASSEMBLY_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
boots.forceMove(drop_location())
to_chat(user, "<span class='notice'>Boots removed.</span>")
boots = null
construction_step = GAUNTLETS_STEP
if(WRENCHED_ASSEMBLY_STEP)
if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Assembly screwed.</span>")
construction_step = SCREWED_ASSEMBLY_STEP
else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Assembly unsecured.</span>")
construction_step = BOOTS_STEP
if(SCREWED_ASSEMBLY_STEP)
if(istype(part, /obj/item/mod/construction/plating)) //Construct
var/obj/item/mod/construction/plating/external_plating = part
if(!user.drop_item())
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core)
core = null
qdel(external_plating)
qdel(src)
user.put_in_hands(mod)
to_chat(user, "<span class='notice'>Suit finished!</span>")
else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "<span class='notice'>Assembly unscrewed.</span>")
construction_step = SCREWED_ASSEMBLY_STEP
update_icon_state()
/obj/item/mod/construction/shell/update_icon_state()
. = ..()
icon_state = "mod-construction_[construction_step]"
/obj/item/mod/construction/shell/Destroy()
QDEL_NULL(core)
QDEL_NULL(helmet)
QDEL_NULL(chestplate)
QDEL_NULL(gauntlets)
QDEL_NULL(boots)
return ..()
/obj/item/mod/construction/shell/handle_atom_del(atom/deleted_atom)
if(deleted_atom == core)
core = null
if(deleted_atom == helmet)
helmet = null
if(deleted_atom == chestplate)
chestplate = null
if(deleted_atom == gauntlets)
gauntlets = null
if(deleted_atom == boots)
boots = null
return ..()
#undef START_STEP
#undef CORE_STEP
#undef SCREWED_CORE_STEP
#undef HELMET_STEP
#undef CHESTPLATE_STEP
#undef GAUNTLETS_STEP
#undef BOOTS_STEP
#undef WRENCHED_ASSEMBLY_STEP
#undef SCREWED_ASSEMBLY_STEP
+746
View File
@@ -0,0 +1,746 @@
/// MODsuits, trade-off between armor and utility
/obj/item/mod
name = "Base MOD"
desc = "You should not see this, yell at a coder!"
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'// figure out how to work with 2 of these
icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
/obj/item/mod/control
name = "MOD control unit"
desc = "The control unit of a Modular Outerwear Device, a powered suit that protects against various environments."
icon_state = "standard-control"
icon_state = "mod_control"
item_state = "mod_control"
base_icon_state = "control"
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
strip_delay = 10 SECONDS
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
actions_types = list(
/datum/action/item_action/mod/deploy,
/datum/action/item_action/mod/activate,
/datum/action/item_action/mod/panel,
/datum/action/item_action/mod/module,
)
resistance_flags = NONE
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
siemens_coefficient = 0.5
var/datum/wires/mod/wires
/// The MOD's theme, decides on some stuff like armor and statistics.
var/datum/mod_theme/theme = /datum/mod_theme/standard
/// Looks of the MOD.
var/skin = "standard"
/// Theme of the MOD TGUI
var/ui_theme = "ntos"
/// If the suit is deployed and turned on.
var/active = FALSE
/// If the suit wire/module hatch is open.
var/open = FALSE
/// If the suit is ID locked.
var/locked = FALSE
/// If the suit is malfunctioning.
var/malfunctioning = FALSE
/// If the suit is currently activating/deactivating.
var/activating = FALSE
/// How long the MOD is electrified for.
var/seconds_electrified = 0
/// If the suit interface is broken.
var/interface_break = FALSE
/// How much module complexity can this MOD carry.
var/complexity_max = DEFAULT_MAX_COMPLEXITY
/// How much module complexity this MOD is carrying.
var/complexity = 0
/// Power usage of the MOD.
var/charge_drain = DEFAULT_CHARGE_DRAIN
/// Slowdown of the MOD when not active.
var/slowdown_inactive = 1.25
/// Slowdown of the MOD when active.
var/slowdown_active = 0.75
/// How long this MOD takes each part to seal.
var/activation_step_time = MOD_ACTIVATION_STEP_TIME
/// Extended description of the theme.
var/extended_desc
/// MOD helmet.
var/obj/item/clothing/head/mod/helmet
/// MOD chestplate.
var/obj/item/clothing/suit/mod/chestplate
/// MOD gauntlets.
var/obj/item/clothing/gloves/mod/gauntlets
/// MOD boots.
var/obj/item/clothing/shoes/mod/boots
/// MOD core.
var/obj/item/mod/core/core
/// Associated list of parts (helmet, chestplate, gauntlets, boots) to their unsealed worn layer.
var/list/mod_parts = list()
/// Associated list of parts that can overslot to their overslot (overslot means the part can cover another layer of clothing).
var/list/overslotting_parts = list()
/// Modules the MOD currently possesses.
var/list/modules = list()
/// Currently used module.
var/obj/item/mod/module/selected_module
/// Person wearing the MODsuit.
var/mob/living/carbon/human/wearer
/// Internal storage in a modsuit
var/obj/item/storage/backpack/modstorage/bag
/// Is it EMP proof?
var/emp_proof = FALSE
/// List of overlays the mod has. Needs to be cut onremoval / module deactivation
var/list/mod_overlays = list()
/// Is the jetpack on so we should make ion effects?
var/jetpack_active = FALSE
/obj/item/mod/control/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core)
. = ..()
if(new_theme)
theme = new_theme
theme = GLOB.mod_themes[theme]
slot_flags = theme.slot_flags
extended_desc = theme.extended_desc
slowdown_inactive = theme.slowdown_inactive
slowdown_active = theme.slowdown_active
complexity_max = theme.complexity_max
ui_theme = theme.ui_theme
charge_drain = theme.charge_drain
wires = new/datum/wires/mod(src)
if(length(req_access))
locked = TRUE
new_core?.install(src)
helmet = new /obj/item/clothing/head/mod(src)
mod_parts += helmet
chestplate = new /obj/item/clothing/suit/mod(src)
chestplate.allowed += theme.allowed_suit_storage
mod_parts += chestplate
gauntlets = new /obj/item/clothing/gloves/mod(src)
mod_parts += gauntlets
boots = new /obj/item/clothing/shoes/mod(src)
mod_parts += boots
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.name = "[theme.name] [part.name]"
part.desc = "[part.desc] [theme.desc]"
part.armor = part.armor.attachArmor(theme.armor_type_2.armor)
part.resistance_flags = theme.resistance_flags
part.flags |= theme.atom_flags //flags like initialization or admin spawning are here, so we cant set, have to add
part.heat_protection = NONE
part.cold_protection = NONE
part.max_heat_protection_temperature = theme.max_heat_protection_temperature
part.min_cold_protection_temperature = theme.min_cold_protection_temperature
part.siemens_coefficient = theme.siemens_coefficient
for(var/obj/item/part as anything in mod_parts)
RegisterSignal(part, COMSIG_OBJ_DECONSTRUCT, PROC_REF(on_part_destruction)) //look into
RegisterSignal(part, COMSIG_PARENT_QDELETING, PROC_REF(on_part_deletion))
set_mod_skin(new_skin || theme.default_skin)
update_speed()
RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
for(var/obj/item/mod/module/module as anything in theme.inbuilt_modules)
module = new module(src)
install(module)
/obj/item/mod/control/Destroy()
if(active)
STOP_PROCESSING(SSobj, src)
for(var/obj/item/mod/module/module as anything in modules)
uninstall(module, deleting = TRUE)
for(var/obj/item/part as anything in mod_parts)
overslotting_parts -= part
if(!QDELETED(helmet))
mod_parts -= helmet
QDEL_NULL(helmet)
if(!QDELETED(chestplate))
mod_parts -= chestplate
QDEL_NULL(chestplate)
if(!QDELETED(gauntlets))
mod_parts -= gauntlets
QDEL_NULL(gauntlets)
if(!QDELETED(boots))
mod_parts -= boots
QDEL_NULL(boots)
if(core)
QDEL_NULL(core)
QDEL_NULL(wires)
wearer = null
selected_module = null
bag = null
modules = null
return ..()
/obj/item/mod/control/examine(mob/user)
. = ..()
if(active)
. += "Charge: [core ? "[get_charge_percent()]%" : "No core"]."
. += "Selected module: [selected_module || "None"]."
if(!open && !active)
if(!wearer)
. += "You could equip it to turn it on."
. += "You could open the cover with a <b>screwdriver</b>."
else if(open)
. += "You could close the cover with a <b>screwdriver</b>."
. += "You could use <b>modules</b> on it to install them."
. += "You could remove modules with a <b>crowbar</b>."
. += "You could update the access lock with an <b>ID</b>."
. += "You could access the wire panel with a <b>wire tool</b>."
if(core)
. += "You could remove [core] with a <b>wrench</b>."
else
. += "You could use a <b>MOD core</b> on it to install one."
. += "<i>[extended_desc]</i>" //god is dead
/obj/item/mod/control/process()
if(seconds_electrified > 0)
seconds_electrified--
if(get_charge() <= 10 && active && !activating) //Sometimes we get power being funky, this should fix it.
power_off()
return PROCESS_KILL
var/malfunctioning_charge_drain = 0
if(malfunctioning)
malfunctioning_charge_drain = rand(1, 20)
subtract_charge((charge_drain + malfunctioning_charge_drain))
update_charge_alert()
for(var/obj/item/mod/module/module as anything in modules)
if(malfunctioning && module.active && prob(5))
module.on_deactivation(display_message = TRUE)
module.on_process()
/obj/item/mod/control/equipped(mob/user, slot)
..()
if(slot == slot_back)
set_wearer(user)
else if(wearer)
unset_wearer()
/obj/item/mod/control/item_action_slot_check(slot)
if(slot == slot_back)
return TRUE
/obj/item/mod/control/on_mob_move(direction, mob/user)
if(!jetpack_active)
return
var/turf/T = get_step(src, GetOppositeDir(direction))
if(!has_gravity(T))
new /obj/effect/particle_effect/ion_trails(T, direction)
/obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
if(!wearer || old_loc != wearer || loc == wearer)
return
clean_up()
/obj/item/mod/control/MouseDrop(atom/over_object)
if(iscarbon(usr))
var/mob/M = usr
if(get_dist(usr, src) > 1) //1 as we want to access it if beside the user
return
if(!over_object)
return
if(ismecha(M.loc))
return
if(!M.restrained() && !M.stat)
playsound(loc, "rustle", 50, TRUE, -5)
if(istype(over_object, /obj/screen/inventory/hand))
for(var/obj/item/part as anything in mod_parts)
if(part.loc != src)
to_chat(wearer, "<span class='warning'>Retract parts first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
return
if(!M.unEquip(src, silent = TRUE))
return
M.put_in_active_hand(src)
else if(bag)
bag.forceMove(usr)
bag.show_to(usr)
add_fingerprint(M)
/obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench)
if(..())
return TRUE
if(seconds_electrified && get_charge() && shock(user))
return TRUE
if(open)
if(!core)
to_chat(user, "<span class='warning'>There is no core!</span>")
return TRUE
wrench.play_tool_sound(src, 100)
if(!wrench.use_tool(src, user, 3 SECONDS) || !open)
return TRUE
if(!core)
return TRUE
wrench.play_tool_sound(src, 100)
core.forceMove(drop_location())
update_charge_alert()
return TRUE
return ..()
/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver)
if(..())
return TRUE
if(active || activating || locate(/mob/living/silicon/ai) in src)
to_chat(user, "<span class='warning'>Deactivate the suit first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
to_chat(user, "<span class='notice'>[open ? "Closing" : "Opening"] cover...</span>")
screwdriver.play_tool_sound(src, 100)
if(screwdriver.use_tool(src, user, 1 SECONDS))
if(active || activating)
to_chat(user, "<span class='warning'>Deactivate the suit first!</span>")
screwdriver.play_tool_sound(src, 100)
to_chat(user, "<span class='notice'>Cover [open ? "closed" : "opened"]</span>")
open = !open
return TRUE
/obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar)
. = ..()
if(!open)
to_chat(user, "<span class='warning'>Open the cover first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!allowed(user))
to_chat(user, "<span class='warning'>Insufficient access!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(seconds_electrified && get_charge() && shock(user))
return FALSE
if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL)
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(length(modules))
var/list/removable_modules = list()
for(var/obj/item/mod/module/module as anything in modules)
if(!module.removable)
continue
removable_modules += module
var/obj/item/mod/module/module_to_remove = input(user, "Which module do you want to pry out?", "Module Removal") as null|anything in removable_modules
if(!module_to_remove?.mod)
return FALSE
uninstall(module_to_remove)
module_to_remove.forceMove(drop_location())
crowbar.play_tool_sound(src, 100)
SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVED, user)
return TRUE
to_chat(user, "<span class='warning'>No modules!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
/obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params)
if(istype(attacking_item, /obj/item/mod/module))
if(!open)
to_chat(user, "<span class='warning'>Open the cover first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
install(attacking_item, user)
SEND_SIGNAL(src, COMSIG_MOD_MODULE_ADDED, user)
return TRUE
else if(istype(attacking_item, /obj/item/mod/core))
if(!open)
to_chat(user, "<span class='warning'>Open the cover first!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(core)
to_chat(user, "<span class='warning'>Core already installed!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
var/obj/item/mod/core/attacking_core = attacking_item
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
user.drop_item()
attacking_core.install(src)
update_charge_alert()
return TRUE
else if(istype(attacking_item, /obj/item/multitool) && open)
if(seconds_electrified && get_charge() && shock(user))
return TRUE
wires.Interact(user)
return TRUE
else if(open && attacking_item.GetID())
update_access(user, attacking_item.GetID())
return TRUE
else if(istype(attacking_item, /obj/item/stock_parts/cell))
if(!core)
to_chat(user, "<span class='warning'>There is no core installed!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
core.on_attackby(attacking_item, user, params)
else if(istype(attacking_item, /obj/item/stack/ore/plasma) || istype(attacking_item, /obj/item/stack/sheet/mineral/plasma))
if(!core)
to_chat(user, "<span class='warning'>There is no core installed!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
core.on_attackby(attacking_item, user, params)
else if(istype(attacking_item, /obj/item/mod/skin_applier))
return ..()
else if(bag && istype(attacking_item))
bag.forceMove(user)
bag.attackby(attacking_item, user, params)
return ..()
/obj/item/mod/control/attack_hand(mob/living/carbon/user)
if(!iscarbon(user))
return
if(loc == user && user.back && user.back == src)
if(bag)
bag.forceMove(user)
bag.show_to(user)
else
..()
/obj/item/mod/control/AltClick(mob/user)
. = ..()
if(ishuman(user) && Adjacent(user) && !user.incapacitated(FALSE, TRUE) && bag)
bag.forceMove(user)
bag.show_to(user)
playsound(loc, "rustle", 50, TRUE, -5)
add_fingerprint(user)
/obj/item/mod/control/proc/can_be_inserted(I, stop_messages)
if(bag)
return bag.can_be_inserted(I, stop_messages)
return FALSE
/obj/item/mod/control/proc/handle_item_insertion(I, prevent_warning)
if(bag)
bag.handle_item_insertion(I, prevent_warning)
/obj/item/mod/control/get_cell()
if(!open)
return
var/obj/item/stock_parts/cell/cell = get_charge_source()
if(!istype(cell))
return
return cell
/obj/item/mod/control/GetAccess()
if(locate(/mob/living/silicon/ai) in src)
return req_access.Copy()
return ..()
/obj/item/mod/control/emag_act(mob/user)
locked = !locked
to_chat(user, "<span class='warning'>Suit access [locked ? "locked" : "unlocked"]")
/obj/item/mod/control/emp_act(severity)
. = ..()
if(!active || !wearer)
return
to_chat(wearer, "<span class='warning'>[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!")
if(emp_proof)
return
selected_module?.on_deactivation(display_message = TRUE)
wearer.apply_damage(10 / severity, BURN, spread_damage = TRUE) //Test this with ion shotguns.
to_chat(wearer, "<span class='danger'>You feel [src] heat up from the EMP, burning you slightly!")
if(wearer.stat < UNCONSCIOUS && prob(10))
wearer.emote("scream")
core.emp_act(severity)
if(prob(50 / severity))
wires.emp_pulse() //3 wires get pulsed. Dangerous to a mod user.
for(var/obj/item/mod/module/holster/H in modules)
H.holstered?.emp_act(severity)
if(bag)
bag.emp_act(severity)
/obj/item/mod/control/dropped(mob/user)
. = ..()
if(!wearer)
return
clean_up()
update_mod_overlays(TRUE)
if(active && !toggle_activate(force_deactivate = TRUE))
return
for(var/obj/item/part as anything in mod_parts)
if(part.loc == src)
continue
retract(null, part)
return ..()
/obj/item/mod/control/update_icon_state()
icon_state = "[skin]-[base_icon_state][active ? "-sealed" : ""]"
return ..()
/obj/item/mod/control/proc/set_wearer(mob/living/carbon/human/user)
if(wearer == user)
// This should also not happen.
// This path is hit when equipping an outfit with visualsOnly, but only sometimes, and this eventually gets called twice.
// I'm not sure this proc should ever be being called by visualsOnly, but it is,
// and this was an emergency patch.
return
else if(!isnull(wearer))
stack_trace("set_wearer() was called with a new wearer without unset_wearer() being called")
wearer = user
SEND_SIGNAL(src, COMSIG_MOD_WEARER_SET, wearer)
RegisterSignal(wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
update_charge_alert()
for(var/obj/item/clothing/C in mod_parts)
C.refit_for_species(wearer.dna.species.name)
update_mod_overlays()
for(var/obj/item/mod/module/module as anything in modules)
module.on_equip()
/obj/item/mod/control/proc/unset_wearer()
for(var/obj/item/mod/module/module as anything in modules)
module.on_unequip()
UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN))
wearer.clear_alert("mod_charge")
SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer)
wearer = null
/obj/item/mod/control/proc/clean_up()
if(active || activating)
for(var/obj/item/mod/module/module as anything in modules)
if(!module.active)
continue
module.on_deactivation(display_message = FALSE)
for(var/obj/item/part as anything in mod_parts)
seal_part(part, seal = FALSE)
for(var/obj/item/part as anything in mod_parts)
retract(null, part)
if(active)
finish_activation(on = FALSE)
var/mob/old_wearer = wearer
unset_wearer()
old_wearer.drop_item()
/obj/item/mod/control/proc/quick_module(mob/user)
if(!length(modules))
return
var/list/display_names = list()
var/list/items = list()
for(var/obj/item/mod/module/module as anything in modules)
if(module.module_type == MODULE_PASSIVE)
continue
display_names[module.name] = module
var/image/module_image = image(icon = module.icon, icon_state = module.icon_state)
if(module == selected_module)
module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected")
else if(module.active)
module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
if(!COOLDOWN_FINISHED(module, cooldown_timer))
module_image.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown"))
items += list(module.name = module_image)
if(!length(items))
return
var/radial_anchor = src
var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = TRUE)
if(!pick)
return
var/module_reference = display_names[pick]
var/obj/item/mod/module/picked_module = module_reference
picked_module.on_select()
/obj/item/mod/control/proc/shock(mob/living/user)
if(!istype(user) || get_charge() < 1)
return FALSE
do_sparks(5, TRUE, src)
var/check_range = TRUE
return electrocute_mob(user, get_charge_source(), src, 1, check_range)
/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user)
for(var/obj/item/mod/module/old_module as anything in modules)
if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
if(user)
to_chat(user, "<span class='warning'>[new_module] incompatible with [old_module]!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
var/complexity_with_module = complexity
complexity_with_module += new_module.complexity
if(complexity_with_module > complexity_max)
if(user)
to_chat(user, "<span class='warning'>[new_module] would make [src] too complex!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(user)
if(!user.drop_item())
to_chat(user, "<span class='warning'>[new_module] is stuck to your hand!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
new_module.forceMove(src)
modules += new_module
complexity += new_module.complexity
new_module.mod = src
new_module.on_install()
if(wearer)
new_module.on_equip()
if(active)
new_module.on_suit_activation()
if(user)
to_chat(user, "<span class='notice'>[new_module] added!")
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE)
modules -= old_module
complexity -= old_module.complexity
if(wearer)
old_module.on_unequip()
if(active)
old_module.on_suit_deactivation(deleting = deleting)
if(old_module.active)
old_module.on_deactivation(display_message = !deleting, deleting = deleting)
old_module.on_uninstall(deleting = deleting)
QDEL_LIST_ASSOC_VAL(old_module.pinned_to)
old_module.mod = null
/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card)
if(!allowed(user))
to_chat(user, "<span class='warning'>Insufficient access!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
req_access = card.access.Copy()
to_chat(user, "<span class='notice'>Access updated!")
/obj/item/mod/control/proc/update_mod_overlays(full_removal = FALSE)
if(!wearer)
return
for(var/I in mod_overlays)
wearer.cut_overlay(I)
mod_overlays -= I
if(full_removal)
return
for(var/obj/item/mod/module/M in modules)
M.add_module_overlay(wearer)
/obj/item/mod/control/proc/get_charge_source()
return core?.charge_source()
/obj/item/mod/control/proc/get_charge()
return core?.charge_amount() || 0
/obj/item/mod/control/proc/get_max_charge()
return core?.max_charge_amount() || 1 //avoid dividing by 0
/obj/item/mod/control/proc/get_charge_percent()
return ((get_charge() / get_max_charge()) * 100)
/obj/item/mod/control/proc/add_charge(amount)
return core?.add_charge(amount) || FALSE
/obj/item/mod/control/proc/subtract_charge(amount)
return core?.subtract_charge(amount) || FALSE
/obj/item/mod/control/proc/check_charge(amount)
return core?.check_charge(amount) || FALSE
/obj/item/mod/control/proc/update_charge_alert()
if(!wearer)
return
if(!core)
wearer.throw_alert("mod_charge", /obj/screen/alert/nocell)
return
core.update_charge_alert()
/obj/item/mod/control/proc/update_speed()
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(all_parts)
/obj/item/mod/control/proc/power_off()
to_chat(wearer, "<span class='warning'>Power cells depleted!")
toggle_activate(wearer, force_deactivate = TRUE)
/obj/item/mod/control/proc/set_mod_color(new_color)
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
wearer?.regenerate_icons()
/obj/item/mod/control/proc/set_mod_skin(new_skin)
if(active)
CRASH("[src] tried to set skin while active!")
skin = new_skin
var/list/used_skin = theme.skins[new_skin]
var/list/skin_updating = mod_parts + src
for(var/obj/item/part as anything in skin_updating)
part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi'
part.icon_state = "[skin]-[part.base_icon_state]"
for(var/obj/item/clothing/part as anything in mod_parts)
var/used_category
if(part == helmet)
used_category = HELMET_FLAGS
if(part == chestplate)
used_category = CHESTPLATE_FLAGS
if(part == gauntlets)
used_category = GAUNTLETS_FLAGS
if(part == boots)
used_category = BOOTS_FLAGS
var/list/category = used_skin[used_category]
part.flags = category[UNSEALED_CLOTHING] || NONE
part.visor_flags = category[SEALED_CLOTHING] || NONE
part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE
part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE
part.flags_cover = category[UNSEALED_COVER] || NONE
part.visor_flags_cover = category[SEALED_COVER] || NONE
if(!category[CAN_OVERSLOT])
if(overslotting_parts[part])
var/obj/item/overslot = overslotting_parts[part]
overslot.forceMove(drop_location())
overslotting_parts -= part
continue
overslotting_parts |= part
wearer?.regenerate_icons()
/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction)
SIGNAL_HANDLER
if(part.loc == src)
return
if(part == core)
core.uninstall()
update_charge_alert()
return
if(part.loc == wearer)
return
if(part in modules)
uninstall(part)
return
if(part in mod_parts)
if(!wearer)
part.forceMove(src)
return
retract(wearer, part, TRUE)
if(active)
INVOKE_ASYNC(src, PROC_REF(toggle_activate), wearer, TRUE)
/obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag)
SIGNAL_HANDLER
if(overslotting_parts[part])
var/obj/item/overslot = overslotting_parts[part]
overslot.forceMove(drop_location())
overslotting_parts[part] = null
if(QDELETED(src))
return
obj_destruction(damage_flag)
/obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction)
SIGNAL_HANDLER
if(overslot != overslotting_parts[source])
return
overslotting_parts[source] = null
/obj/item/mod/control/proc/on_part_deletion(obj/item/part) //the part doesnt count as being qdeleted, so our destroying does an infinite loop, fix later
SIGNAL_HANDLER
if(QDELETED(src))
return
qdel(src)
/obj/item/mod/control/water_act(volume, temperature, source, method)
if(HAS_TRAIT(src, TRAIT_OIL_SLICKED)) //Overide base to work right
slowdown_active = theme.slowdown_active
slowdown_inactive = theme.slowdown_inactive
update_speed()
remove_atom_colour(FIXED_COLOUR_PRIORITY)
REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion")
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.regenerate_icons()
+298
View File
@@ -0,0 +1,298 @@
/obj/item/mod/core
name = "MOD core"
desc = "A non-functional MOD core. Inform the admins if you see this."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
icon_state = "mod-core"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
/// MOD unit we are powering.
var/obj/item/mod/control/mod
/obj/item/mod/core/Destroy()
if(mod)
uninstall()
return ..()
/obj/item/mod/core/proc/install(obj/item/mod/control/mod_unit)
mod = mod_unit
mod.core = src
forceMove(mod)
/obj/item/mod/core/proc/uninstall()
mod.core = null
mod = null
/obj/item/mod/core/proc/charge_source()
return
/obj/item/mod/core/proc/charge_amount()
return 0
/obj/item/mod/core/proc/max_charge_amount()
return 1
/obj/item/mod/core/proc/add_charge(amount)
return FALSE
/obj/item/mod/core/proc/subtract_charge(amount)
return FALSE
/obj/item/mod/core/proc/check_charge(amount)
return FALSE
/obj/item/mod/core/proc/update_charge_alert()
mod.wearer.clear_alert("mod_charge")
/obj/item/mod/core/infinite //Admin only.
name = "MOD infinite core"
icon_state = "mod-core-infinite"
desc = "A fusion core using the rare Infinium to sustain enough energy for the lifetime of the MOD's user. \
This might be because of the slowly killing radiation inside, but those are just rumors."
/obj/item/mod/core/infinite/charge_source()
return src
/obj/item/mod/core/infinite/charge_amount()
return INFINITY
/obj/item/mod/core/infinite/max_charge_amount()
return INFINITY
/obj/item/mod/core/infinite/add_charge(amount)
return TRUE
/obj/item/mod/core/infinite/subtract_charge(amount)
return TRUE
/obj/item/mod/core/infinite/check_charge(amount)
return TRUE
/obj/item/mod/core/standard
name = "MOD standard core"
desc = "Using Thermo Generators to store and amplify power in the form of heat, this core acts as a power cell for your modsuit." //We don't have etherals
icon_state = "mod-core-standard"
/// Installed cell.
var/obj/item/stock_parts/cell/cell
/obj/item/mod/core/standard/Destroy()
if(cell)
QDEL_NULL(cell)
return ..()
/obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit)
. = ..()
if(cell)
install_cell(cell)
RegisterSignal(mod, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(mod, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
RegisterSignal(mod, COMSIG_MOD_WEARER_SET, PROC_REF(on_wearer_set))
if(mod.wearer)
on_wearer_set(mod, mod.wearer)
/obj/item/mod/core/standard/uninstall()
if(!QDELETED(cell))
cell.forceMove(drop_location())
UnregisterSignal(mod, list(COMSIG_PARENT_EXAMINE, COMSIG_ATOM_ATTACK_HAND, COMSIG_MOD_WEARER_SET))
if(mod.wearer)
on_wearer_unset(mod, mod.wearer)
return ..()
/obj/item/mod/core/proc/on_attackby(obj/item/attacking_item, mob/user, params)
return
/obj/item/mod/core/standard/charge_source()
return cell
/obj/item/mod/core/standard/charge_amount()
var/obj/item/stock_parts/cell/charge_source = charge_source()
return charge_source?.charge || 0
/obj/item/mod/core/standard/max_charge_amount(amount)
var/obj/item/stock_parts/cell/charge_source = charge_source()
return charge_source?.maxcharge || 1
/obj/item/mod/core/standard/add_charge(amount)
var/obj/item/stock_parts/cell/charge_source = charge_source()
if(!charge_source)
return FALSE
return charge_source.give(amount)
/obj/item/mod/core/standard/subtract_charge(amount)
var/obj/item/stock_parts/cell/charge_source = charge_source()
if(!charge_source)
return FALSE
return charge_source.use(amount, TRUE)
/obj/item/mod/core/standard/check_charge(amount)
return charge_amount() >= amount
/obj/item/mod/core/standard/update_charge_alert()
var/obj/item/stock_parts/cell/charge_source = charge_source()
if(!charge_source)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/nocell)
return
var/remaining_cell = charge_amount() / max_charge_amount()
switch(remaining_cell)
if(0.75 to INFINITY)
mod.wearer.clear_alert("mod_charge")
if(0.5 to 0.75)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 1)
if(0.25 to 0.5)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 2)
if(0.01 to 0.25)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 3)
else
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/emptycell)
/obj/item/mod/core/standard/emp_act(severity)
cell?.emp_act(severity)
/obj/item/mod/core/standard/proc/install_cell(new_cell)
cell = new_cell
cell.forceMove(src)
RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
/obj/item/mod/core/standard/proc/uninstall_cell()
if(!cell)
return
cell = null
UnregisterSignal(src, COMSIG_ATOM_EXITED)
/obj/item/mod/core/standard/proc/on_exit(datum/source, obj/item/stock_parts/cell, direction)
SIGNAL_HANDLER
if(!istype(cell) || cell.loc == src)
return
uninstall_cell()
/obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text)
SIGNAL_HANDLER
if(!mod.open)
return
examine_text += cell ? "You could remove the cell with an empty hand." : "You could use a cell on it to install one."
/obj/item/mod/core/standard/proc/on_attack_hand(datum/source, mob/living/user)
SIGNAL_HANDLER
if(mod.seconds_electrified && charge_amount() && mod.shock(user))
return COMPONENT_CANCEL_ATTACK_CHAIN
if(mod.open && mod.loc == user)
INVOKE_ASYNC(src, PROC_REF(mod_uninstall_cell), user)
return COMPONENT_CANCEL_ATTACK_CHAIN
return NONE
/obj/item/mod/core/standard/proc/mod_uninstall_cell(mob/living/user)
if(!cell)
to_chat(user, "<span class='warning'>No cell installed!</span>")
return
if(!do_after(user, 1.5 SECONDS, target = user))
return
to_chat(user, "<span class='notice'>You remove the cell.</span>")
playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
var/obj/item/cell_to_move = cell
cell_to_move.forceMove(drop_location())
user.put_in_hands(cell_to_move)
mod.update_charge_alert()
/obj/item/mod/core/standard/on_attackby(obj/item/attacking_item, mob/user, params)
if(istype(attacking_item, /obj/item/stock_parts/cell))
if(!mod.open)
to_chat(user, "<span class='warning'>Open the cover first!</span>")
playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return NONE
if(cell)
to_chat(user, "<span class='warning'>Cell already installed!</span>")
playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return COMPONENT_NO_AFTERATTACK
user.drop_item()
install_cell(attacking_item)
to_chat(user, "<span class='notice'>You install the cell.</span>")
playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
mod.update_charge_alert()
return COMPONENT_NO_AFTERATTACK
return NONE
/obj/item/mod/core/standard/proc/on_wearer_set(datum/source, mob/user)
SIGNAL_HANDLER
RegisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
RegisterSignal(mod, COMSIG_MOD_WEARER_UNSET, PROC_REF(on_wearer_unset))
/obj/item/mod/core/standard/proc/on_wearer_unset(datum/source, mob/user)
SIGNAL_HANDLER
UnregisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT)
UnregisterSignal(mod, COMSIG_MOD_WEARER_UNSET)
/obj/item/mod/core/standard/proc/on_borg_charge(datum/source, amount)
SIGNAL_HANDLER
add_charge(amount)
mod.update_charge_alert()
/obj/item/mod/core/plasma
name = "MOD plasma core"
desc = "Nanotrasen's attempt at capitalizing on their plasma research. These plasma cores are refueled \
through plasma fuel, allowing for easy continued use by their mining squads."
icon_state = "mod-core-plasma"
/// How much charge we can store.
var/maxcharge = 10000
/// How much charge we are currently storing.
var/charge = 10000
/// Associated list of charge sources, only stacks allowed.
var/list/charger_list = list(/obj/item/stack/ore/plasma, /obj/item/stack/sheet/mineral/plasma)
/obj/item/mod/core/plasma/attackby(obj/item/attacking_item, mob/user, params)
if(charge_plasma(attacking_item, user))
return TRUE
return ..()
/obj/item/mod/core/plasma/charge_source()
return src
/obj/item/mod/core/plasma/charge_amount()
return charge
/obj/item/mod/core/plasma/max_charge_amount()
return maxcharge
/obj/item/mod/core/plasma/add_charge(amount)
charge = min(maxcharge, charge + amount)
return TRUE
/obj/item/mod/core/plasma/subtract_charge(amount)
charge = max(0, charge - amount)
return TRUE
/obj/item/mod/core/plasma/check_charge(amount)
return charge_amount() >= amount
/obj/item/mod/core/plasma/update_charge_alert()
var/remaining_plasma = charge_amount() / max_charge_amount()
switch(remaining_plasma)
if(0.75 to INFINITY)
mod.wearer.clear_alert("mod_charge")
if(0.5 to 0.75)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 1)
if(0.25 to 0.5)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 2)
if(0.01 to 0.25)
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 3)
else
mod.wearer.throw_alert("mod_charge", /obj/screen/alert/emptycell)
/obj/item/mod/core/plasma/on_attackby(obj/item/attacking_item, mob/user, params)
charge_plasma(attacking_item, user)
/obj/item/mod/core/plasma/proc/charge_plasma(obj/item/stack/plasma, mob/user)
var/charge_given = is_type_in_list(plasma, charger_list)
if(!charge_given)
return FALSE
var/uses_needed = min(plasma.amount, ((max_charge_amount() - charge_amount()) / 2000))
if(!plasma.use(uses_needed))
return FALSE
add_charge(uses_needed * 2000)
to_chat(user, "<span class='notice'>You insert [plasma] in [src], recharging it.</span>")
return TRUE
+42
View File
@@ -0,0 +1,42 @@
/obj/item/mod/skin_applier
name = "MOD skin applier"
desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to standard modsuits."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
icon_state = "skinapplier"
var/skin = "civilian"
var/make_spaceproof = FALSE //Used on the miner asteroid skin to make the suit spaceproof when upgrading.
var/compatible_theme = /datum/mod_theme/standard
/obj/item/mod/skin_applier/Initialize(mapload)
. = ..()
name = "MOD [skin] skin applier"
/obj/item/mod/skin_applier/pre_attack(atom/attacked_atom, mob/living/user, params)
if(!ismodcontrol(attacked_atom))
return ..()
var/obj/item/mod/control/mod = attacked_atom
if(mod.active || mod.activating)
to_chat(user, "<span class='warning'>Deactivate the suit!</span>")
return TRUE
if(!istype(mod.theme, compatible_theme))
to_chat(user, "<span class='warning'>Theme is not compatible!</span>")
return TRUE
mod.set_mod_skin(skin)
if(make_spaceproof)
mod.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
for(var/obj/item/clothing/C in mod.mod_parts)
C.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
to_chat(user, "<span class='notice'>You apply the theme to [mod].</span>")
qdel(src)
return TRUE
/obj/item/mod/skin_applier/asteroid
skin = "asteroid"
compatible_theme = /datum/mod_theme/mining
desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to mining modsuits, and makes them spaceproof. Enjoy the days when you had MODsuits on the asteroid. Wait a minute."
make_spaceproof = TRUE
/obj/item/mod/skin_applier/corpsman
skin = "corpsman"
compatible_theme = /datum/mod_theme/medical
desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to medical modsuits. You look like a corpse, man! Or was it a corps man?"
File diff suppressed because it is too large Load Diff
+454
View File
@@ -0,0 +1,454 @@
/obj/item/mod/control/pre_equipped
/// The skin we apply to the suit, defaults to the default_skin of the theme.
var/applied_skin
/// The MOD core we apply to the suit.
var/applied_core = /obj/item/mod/core/standard
/// The cell we apply to the core. Only applies to standard core suits.
var/applied_cell = /obj/item/stock_parts/cell/high
/// List of modules we spawn with.
var/list/applied_modules = list()
/// Modules that we pin when the suit is installed for the first time, for convenience, can be applied or theme inbuilt modules.
var/list/default_pins = list()
/obj/item/mod/control/pre_equipped/Initialize(mapload, new_theme, new_skin, new_core, new_access)
for(var/module_to_pin in default_pins)
default_pins[module_to_pin] = list()
new_skin = applied_skin
new_core = new applied_core(src)
if(istype(new_core, /obj/item/mod/core/standard))
var/obj/item/mod/core/standard/cell_core = new_core
cell_core.cell = new applied_cell()
. = ..()
for(var/obj/item/mod/module/module as anything in applied_modules)
module = new module(src)
install(module)
/obj/item/mod/control/pre_equipped/set_wearer(mob/living/carbon/human/user)
. = ..()
for(var/obj/item/mod/module/module as anything in modules)
if(!default_pins[module.type]) //this module isnt meant to be pinned by default
continue
if(UID(wearer) in default_pins[module.type]) //if we already had pinned once to this user, don care anymore
continue
default_pins[module.type] += UID(wearer)
module.pin(wearer)
/obj/item/mod/control/pre_equipped/uninstall(obj/item/mod/module/old_module, deleting)
. = ..()
if(default_pins[old_module.type])
default_pins -= old_module
/obj/item/mod/control/pre_equipped/standard
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
)
/obj/item/mod/control/pre_equipped/engineering
theme = /datum/mod_theme/engineering
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/magboot,
)
default_pins = list(
/obj/item/mod/module/magboot,
)
/obj/item/mod/control/pre_equipped/atmospheric
theme = /datum/mod_theme/atmospheric
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/t_ray,
/obj/item/mod/module/magboot,
)
default_pins = list(
/obj/item/mod/module/magboot,
)
/obj/item/mod/control/pre_equipped/advanced
theme = /datum/mod_theme/advanced
applied_cell = /obj/item/stock_parts/cell/super
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/jetpack/advanced,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/magboot/advanced,
)
/obj/item/mod/control/pre_equipped/loader
theme = /datum/mod_theme/loader
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/stamp,
)
default_pins = list(
/obj/item/mod/module/clamp/loader,
/obj/item/mod/module/magnet,
/obj/item/mod/module/hydraulic,
)
/obj/item/mod/control/pre_equipped/mining
theme = /datum/mod_theme/mining
applied_core = /obj/item/mod/core/plasma
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/gps,
/obj/item/mod/module/orebag,
/obj/item/mod/module/clamp,
/obj/item/mod/module/drill,
)
default_pins = list(
/obj/item/mod/module/gps,
/obj/item/mod/module/drill,
/obj/item/mod/module/sphere_transform,
)
/obj/item/mod/control/pre_equipped/mining/vendor //visit robotics.
theme = /datum/mod_theme/mining
applied_core = /obj/item/mod/core/plasma
applied_modules = list(
/obj/item/mod/module/storage,
)
default_pins = list(
/obj/item/mod/module/sphere_transform,
)
/obj/item/mod/control/pre_equipped/mining/asteroid //The asteroid skin, as that one looks more space worthy / older. Good for space ruins.
applied_skin = "asteroid"
/obj/item/mod/control/pre_equipped/medical
theme = /datum/mod_theme/medical
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/injector,
)
/obj/item/mod/control/pre_equipped/rescue
theme = /datum/mod_theme/rescue
applied_cell = /obj/item/stock_parts/cell/super
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/injector,
/obj/item/mod/module/defibrillator,
)
default_pins = list(
/obj/item/mod/module/defibrillator,
)
/obj/item/mod/control/pre_equipped/research
theme = /datum/mod_theme/research
applied_cell = /obj/item/stock_parts/cell/super
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/t_ray,
)
/obj/item/mod/control/pre_equipped/security
theme = /datum/mod_theme/security
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/dispenser/mirage,
/obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/safeguard
theme = /datum/mod_theme/safeguard
applied_cell = /obj/item/stock_parts/cell/super
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/dispenser/mirage,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/holster,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
)
/obj/item/mod/control/pre_equipped/magnate
theme = /datum/mod_theme/magnate
applied_cell = /obj/item/stock_parts/cell/hyper
applied_modules = list(
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/jetpack/advanced,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF // Theft targets should be hard to destroy
/obj/item/mod/control/pre_equipped/magnate/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(alert_admins_on_destroy))
/obj/item/mod/control/pre_equipped/cosmohonk
theme = /datum/mod_theme/cosmohonk
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/waddle,
/obj/item/mod/module/bikehorn,
)
/obj/item/mod/control/pre_equipped/traitor
theme = /datum/mod_theme/syndicate
applied_cell = /obj/item/stock_parts/cell/super
applied_modules = list(
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/jetpack,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/noslip,
)
default_pins = list(
/obj/item/mod/module/armor_booster,
/obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/traitor_elite
theme = /datum/mod_theme/elite
applied_cell = /obj/item/stock_parts/cell/bluespace
applied_modules = list(
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/noslip,
/obj/item/mod/module/flashlight,
)
default_pins = list(
/obj/item/mod/module/armor_booster,
/obj/item/mod/module/jetpack/advanced,
)
/obj/item/mod/control/pre_equipped/nuclear
theme = /datum/mod_theme/syndicate
applied_cell = /obj/item/stock_parts/cell/hyper
req_access = list(ACCESS_SYNDICATE)
applied_modules = list(
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/dna_lock/emp_shield,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/noslip,
)
default_pins = list(
/obj/item/mod/module/armor_booster,
/obj/item/mod/module/jetpack/advanced,
)
/obj/item/mod/control/pre_equipped/elite
theme = /datum/mod_theme/elite
applied_cell = /obj/item/stock_parts/cell/bluespace
req_access = list(ACCESS_SYNDICATE)
applied_modules = list(
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/dna_lock/emp_shield,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/noslip,
)
default_pins = list(
/obj/item/mod/module/armor_booster,
/obj/item/mod/module/jetpack/advanced,
)
/obj/item/mod/control/pre_equipped/prototype
theme = /datum/mod_theme/prototype
req_access = list()
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/tether,
)
default_pins = list(
/obj/item/mod/module/tether,
/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype,
)
/obj/item/mod/control/pre_equipped/responsory
theme = /datum/mod_theme/responsory
applied_cell = /obj/item/stock_parts/cell/hyper
req_access = list(ACCESS_CENT_GENERAL)
applied_modules = list(
/obj/item/mod/module/storage/syndicate, //Yes yes syndicate tech in ert but they need the storage
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/ert_camera,
)
/// The insignia type, insignias show what sort of member of the ERT you're dealing with.
var/insignia_type = /obj/item/mod/module/insignia
/// Additional module we add, as a treat.
var/additional_module
/obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin, new_core)
applied_modules.Insert(1, insignia_type)
if(additional_module)
applied_modules += additional_module
default_pins += additional_module
return ..()
/obj/item/mod/control/pre_equipped/responsory/commander
insignia_type = /obj/item/mod/module/insignia/commander
additional_module = /obj/item/mod/module/power_kick
/obj/item/mod/control/pre_equipped/responsory/security
insignia_type = /obj/item/mod/module/insignia/security
additional_module = /obj/item/mod/module/dispenser/mirage
/obj/item/mod/control/pre_equipped/responsory/engineer
insignia_type = /obj/item/mod/module/insignia/engineer
additional_module = /obj/item/mod/module/anomaly_locked/kinesis/prebuilt //This can only end well.
/obj/item/mod/control/pre_equipped/responsory/medic
insignia_type = /obj/item/mod/module/insignia/medic
additional_module = /obj/item/mod/module/defibrillator
/obj/item/mod/control/pre_equipped/responsory/janitor
insignia_type = /obj/item/mod/module/insignia/janitor
additional_module = /obj/item/mod/module/clamp
/obj/item/mod/control/pre_equipped/responsory/clown
insignia_type = /obj/item/mod/module/insignia/clown
additional_module = /obj/item/mod/module/bikehorn
/obj/item/mod/control/pre_equipped/responsory/chaplain
insignia_type = /obj/item/mod/module/insignia/chaplain
additional_module = /obj/item/mod/module/injector
/obj/item/mod/control/pre_equipped/responsory/inquisitory //Diffrent look, as well as magic proof on TG. We don't have the magic proof stuff here, but it's perfect for inqusitors. Or if you want to give your ERT a fancy look.
applied_skin = "inquisitory"
/obj/item/mod/control/pre_equipped/responsory/inquisitory/commander
insignia_type = /obj/item/mod/module/insignia/commander
additional_module = /obj/item/mod/module/power_kick
/obj/item/mod/control/pre_equipped/responsory/inquisitory/security
insignia_type = /obj/item/mod/module/insignia/security
additional_module = /obj/item/mod/module/dispenser/mirage
/obj/item/mod/control/pre_equipped/responsory/inquisitory/medic
insignia_type = /obj/item/mod/module/insignia/medic
additional_module = /obj/item/mod/module/defibrillator
/obj/item/mod/control/pre_equipped/responsory/inquisitory/chaplain
insignia_type = /obj/item/mod/module/insignia/chaplain
additional_module = /obj/item/mod/module/power_kick //JUDGEMENT
/obj/item/mod/control/pre_equipped/apocryphal
theme = /datum/mod_theme/apocryphal
applied_cell = /obj/item/stock_parts/cell/bluespace
req_access = list(ACCESS_CENT_SPECOPS)
applied_modules = list(
/obj/item/mod/module/storage/bluespace,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/magboot/advanced,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/magboot/advanced,
)
/obj/item/mod/control/pre_equipped/apocryphal/officer
applied_modules = list(
/obj/item/mod/module/storage/bluespace,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/power_kick,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/power_kick, //If you are not drop kicking a xenomorph, what are you doing as an DS commander?
)
/obj/item/mod/control/pre_equipped/corporate
theme = /datum/mod_theme/corporate
applied_core = /obj/item/mod/core/infinite
req_access = list(ACCESS_CENT_SPECOPS)
applied_modules = list(
/obj/item/mod/module/storage/bluespace,
/obj/item/mod/module/dna_lock/emp_shield,
/obj/item/mod/module/status_readout,
/obj/item/mod/module/anomaly_locked/kinesis/plus,
/obj/item/mod/module/magboot/advanced,
)
default_pins = list(
/obj/item/mod/module/anomaly_locked/kinesis/plus,
/obj/item/mod/module/magboot/advanced,
)
/obj/item/mod/control/pre_equipped/debug
theme = /datum/mod_theme/debug
applied_core = /obj/item/mod/core/infinite
applied_modules = list( //one of every type of module, for testing if they all work correctly // boy this isn't even 25% the modules
/obj/item/mod/module/storage/bluespace,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/bikehorn,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/injector,
)
/obj/item/mod/control/pre_equipped/administrative
theme = /datum/mod_theme/administrative
applied_core = /obj/item/mod/core/infinite
applied_modules = list(
/obj/item/mod/module/storage/bluespace,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/welding,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/stealth/ninja,
)
default_pins = list(
/obj/item/mod/module/stealth/ninja,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/jetpack/advanced,
)
//these exist for the prefs menu
/obj/item/mod/control/pre_equipped/empty
/obj/item/mod/control/pre_equipped/empty/syndicate
theme = /datum/mod_theme/syndicate
/obj/item/mod/control/pre_equipped/empty/syndicate/honkerative
applied_skin = "honkerative"
/obj/item/mod/control/pre_equipped/empty/elite
theme = /datum/mod_theme/elite
INITIALIZE_IMMEDIATE(/obj/item/mod/control/pre_equipped/empty)
+85
View File
@@ -0,0 +1,85 @@
/obj/item/mod/control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "MODsuit", name, 400, 525, master_ui, state)
ui.open()
/obj/item/mod/control/ui_data(mob/user)
var/data = list()
data["interface_break"] = interface_break
data["malfunctioning"] = malfunctioning
data["open"] = open
data["active"] = active
data["locked"] = locked
data["complexity"] = complexity
data["selected_module"] = selected_module?.name
data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant"
data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job"
data["core"] = core?.name
data["charge"] = get_charge_percent()
data["modules"] = list()
for(var/obj/item/mod/module/module as anything in modules)
var/list/module_data = list(
"module_name" = module.name,
"description" = module.desc,
"module_type" = module.module_type,
"module_active" = module.active,
"pinned" = module.pinned_to[UID(user)], //might just want user here
"idle_power" = module.idle_power_cost,
"active_power" = module.active_power_cost,
"use_power" = module.use_power_cost,
"module_complexity" = module.complexity,
"cooldown_time" = module.cooldown_time,
"cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS),
"ref" = module.module_UID, //might just want user here
"id" = module.tgui_id,
"configuration_data" = module.get_configuration()
)
module_data += module.add_ui_data()
data["modules"] += list(module_data)
return data
/obj/item/mod/control/ui_static_data(mob/user)
var/data = list()
data["ui_theme"] = ui_theme
data["control"] = name
data["complexity_max"] = complexity_max
data["helmet"] = helmet?.name
data["chestplate"] = chestplate?.name
data["gauntlets"] = gauntlets?.name
data["boots"] = boots?.name
return data
/obj/item/mod/control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(locked && !allowed(usr))
to_chat(usr, "<span class='warning'>Insufficient access!</span>")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(malfunctioning && prob(75))
to_chat(usr, "<span class='warning'>ERROR!</span>")
return
switch(action)
if("lock")
locked = !locked
to_chat(usr, "<span class='notice'>ID [locked ? "locked" : "unlocked"].</span>")
if("activate")
toggle_activate(usr)
if("select")
var/obj/item/mod/module/module = locateUID(params["ref"])
if(!module)
return
module.on_select()
if("configure")
var/obj/item/mod/module/module = locateUID(params["ref"])
if(!module)
return
module.configure_edit(params["key"], params["value"])
if("pin")
var/obj/item/mod/module/module = locateUID(params["ref"])
if(!module)
return
module.pin(usr)
return TRUE
+431
View File
@@ -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 ..()
+266
View File
@@ -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)
+408
View File
@@ -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 ..()
+441
View File
@@ -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)
+129
View File
@@ -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"
+115
View File
@@ -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)
+541
View File
@@ -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)
+93
View File
@@ -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