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