diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index b67a31e2c95..302e8daf2cf 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -64,8 +64,6 @@
#define BURDENED /datum/mutation/human/burdened
#define HONORBOUND /datum/mutation/human/honorbound
-#define TK_MOD /datum/mutation/human/telekinesis/mod
-
#define UE_CHANGED "ue changed"
#define UI_CHANGED "ui changed"
#define UF_CHANGED "uf changed"
diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm
index 1e0f4e2b3e1..bd7aed1eebb 100644
--- a/code/_onclick/telekinesis.dm
+++ b/code/_onclick/telekinesis.dm
@@ -199,7 +199,7 @@
return TRUE
/obj/item/tk_grab/proc/check_if_focusable(obj/target)
- if(!tk_user || !istype(tk_user) || QDELETED(target) || !istype(target) || !(tk_user.dna.check_mutation(TK) || tk_user.dna.check_mutation(TK_MOD)))
+ if(!tk_user || !istype(tk_user) || QDELETED(target) || !istype(target) || !tk_user.dna.check_mutation(TK))
qdel(src)
return
if(!tkMaxRangeCheck(tk_user, target) || target.anchored || !isturf(target.loc))
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index 3fbbef7d560..7a7a53bcec1 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -128,3 +128,8 @@
volume = 40
falloff_distance = 5
falloff_exponent = 20
+
+/datum/looping_sound/gravgen/kinesis
+ volume = 20
+ falloff_distance = 2
+ falloff_exponent = 5
diff --git a/code/datums/mutations/telekinesis.dm b/code/datums/mutations/telekinesis.dm
index 05134457334..234cc637495 100644
--- a/code/datums/mutations/telekinesis.dm
+++ b/code/datums/mutations/telekinesis.dm
@@ -38,14 +38,3 @@
if(!tkMaxRangeCheck(source, target) || source.z != target.z)
return
return target.attack_tk(source)
-
-/datum/mutation/human/telekinesis/mod
- name = "Kinesis"
- desc = "A modification that allows the wearer of a MODsuit to interact with objects through thought with the kinesis module."
- locked = TRUE
- text_gain_indication = null
- limb_req = null
- instability = 0
-
-/datum/mutation/human/telekinesis/mod/get_visual_indicator()
- return
diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm
index d9018c50271..fd41f739e33 100644
--- a/code/game/objects/effects/effect_system/effects_other.dm
+++ b/code/game/objects/effects/effect_system/effects_other.dm
@@ -81,6 +81,9 @@
/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I)
I.setDir(holder.dir)
+/datum/effect_system/trail_follow/ion/grav_allowed
+ nograv_required = FALSE
+
//Reagent-based explosion effect
/datum/effect_system/reagents_explosion
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index 28a80874165..e7b2c84494e 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -70,6 +70,12 @@
chameleon_extras = /obj/item/stamp/clown
implants = list(/obj/item/implant/sad_trombone)
+/datum/outfit/job/clown/mod
+ name = "Clown (MODsuit)"
+
+ suit_store = /obj/item/tank/internals/oxygen
+ back = /obj/item/mod/control/pre_equipped/cosmohonk
+
/datum/outfit/job/clown/pre_equip(mob/living/carbon/human/H, visualsOnly)
. = ..()
if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS))
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index e02f119b086..20fdbcaa864 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -231,7 +231,14 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
//The helmet is necessary because /obj/item/clothing/head/helmet/sec is overwritten in the chameleon list by the standard helmet, which has the same name and icon state
implants = list(/obj/item/implant/mindshield)
+/datum/outfit/job/security/mod
+ name = "Security Officer (MODsuit)"
+ suit_store = /obj/item/tank/internals/oxygen
+ back = /obj/item/mod/control/pre_equipped/security
+ suit = null
+ head = null
+ mask = /obj/item/clothing/mask/gas/sechailer
/obj/item/radio/headset/headset_sec/alt/department/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob_spawn/ghost_roles/space_roles.dm b/code/modules/mob_spawn/ghost_roles/space_roles.dm
index 43164d7d677..a6e7d044e81 100644
--- a/code/modules/mob_spawn/ghost_roles/space_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/space_roles.dm
@@ -54,6 +54,12 @@
gloves = /obj/item/clothing/gloves/color/fyellow/old
l_pocket = /obj/item/tank/internals/emergency_oxygen
+/datum/outfit/oldeng/mod
+ name = "Ancient Engineer (MODsuit)"
+ suit_store = /obj/item/tank/internals/oxygen
+ back = /obj/item/mod/control/pre_equipped/prototype
+ mask = /obj/item/clothing/mask/breath
+
/obj/effect/mob_spawn/ghost_role/human/oldsci
name = "old cryogenics pod"
desc = "A humming cryo pod. You can barely recognise a science uniform underneath the built up ice. The machine is attempting to wake up its occupant."
diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm
index 99b7bf61f44..2bf9d159ea6 100644
--- a/code/modules/mod/mod_construction.dm
+++ b/code/modules/mod/mod_construction.dm
@@ -242,6 +242,7 @@
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "suit finished")
var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_armor.theme, null, core)
+ core = null
qdel(src)
user.put_in_hands(mod)
else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index 3f3b3390ccd..d2b12743fcb 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -381,7 +381,7 @@
balloon_alert(user, "not painted!")
return TRUE
else if(open && attacking_item.GetID())
- update_access(user, attacking_item)
+ update_access(user, attacking_item.GetID())
return TRUE
return ..()
diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm
index 60bc33fb77a..8a533ea56ab 100644
--- a/code/modules/mod/mod_core.dm
+++ b/code/modules/mod/mod_core.dm
@@ -93,7 +93,7 @@
var/obj/item/stock_parts/cell/charge_source = charge_source()
if(!charge_source)
return FALSE
- return charge_source.use(amount)
+ return charge_source.use(amount, TRUE)
/obj/item/mod/core/standard/update_charge_alert()
var/obj/item/stock_parts/cell/charge_source = charge_source()
@@ -158,9 +158,9 @@
return
mod.balloon_alert(user, "cell removed")
playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
- uninstall_cell()
- cell.forceMove(drop_location())
- user.put_in_hands(cell)
+ 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/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index a05e64d2bb3..d5cdeec553f 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -522,7 +522,6 @@
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
slowdown_inactive = 0.75
slowdown_active = 0.25
- inbuilt_modules = list(/obj/item/mod/module/hat_stabilizer)
skins = list(
"magnate" = list(
HELMET_LAYER = NECK_LAYER,
@@ -764,7 +763,7 @@
slowdown_inactive = 2
slowdown_active = 1.5
ui_theme = "hackerman"
- inbuilt_modules = list(/obj/item/mod/module/kinesis)
+ inbuilt_modules = list(/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype)
skins = list(
"prototype" = list(
HELMET_LAYER = null,
@@ -912,7 +911,6 @@
siemens_coefficient = 0
slowdown_inactive = 0.5
slowdown_active = 0
- inbuilt_modules = list(/obj/item/mod/module/hat_stabilizer)
skins = list(
"corporate" = list(
HELMET_LAYER = null,
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index e6af768f845..bf66e9cef77 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -118,7 +118,8 @@
applied_cell = /obj/item/stock_parts/cell/hyper
initial_modules = list(
/obj/item/mod/module/storage/large_capacity,
- /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/hat_stabilizer,
+ /obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/holster,
/obj/item/mod/module/pathfinder,
)
@@ -148,7 +149,7 @@
initial_modules = list(
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/welding,
- /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
/obj/item/mod/module/injector,
@@ -161,7 +162,7 @@
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
- /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
/obj/item/mod/module/injector,
@@ -178,7 +179,7 @@
/obj/item/mod/control/pre_equipped/prototype
theme = /datum/mod_theme/prototype
- applied_cell = /obj/item/stock_parts/cell/upgraded
+ applied_cell = /obj/item/stock_parts/cell/high/plus
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
@@ -278,6 +279,7 @@
applied_core = /obj/item/mod/core/infinite
initial_modules = list(
/obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/hat_stabilizer,
/obj/item/mod/module/holster,
)
diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm
index ee19c1f8a1a..8ba92f57fe6 100644
--- a/code/modules/mod/modules/_module.dm
+++ b/code/modules/mod/modules/_module.dm
@@ -1,4 +1,4 @@
-///MOD module - A special device installed in a MODsuit allowing the suit to do new stuff.
+///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'
@@ -159,6 +159,7 @@
balloon_alert(mod.wearer, "on cooldown!")
return FALSE
if(!check_power(use_power_cost))
+ balloon_alert(mod.wearer, "not enough charge!")
return FALSE
if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
//specifically a to_chat because the user is phased out.
@@ -207,6 +208,7 @@
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
@@ -292,3 +294,90 @@
action = new(mod, src, user)
action.Grant(user)
pinned_to[user] = action
+
+///Anomaly Locked - Causes the module to not function without an anomaly.
+/obj/item/mod/module/anomaly_locked
+ name = "MOD anomaly locked module"
+ desc = "A form of a module, locked behind an anomalous core to function."
+ incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
+ /// The core item the module runs off.
+ var/obj/item/assembly/signaler/anomaly/core
+ /// Accepted types of anomaly cores.
+ var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
+ /// If this one starts with a core in.
+ var/prebuilt = FALSE
+
+/obj/item/mod/module/anomaly_locked/Initialize(mapload)
+ . = ..()
+ if(!prebuilt || !length(accepted_anomalies))
+ return
+ var/core_path = pick(accepted_anomalies)
+ core = new core_path(src)
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/Destroy()
+ QDEL_NULL(core)
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/examine(mob/user)
+ . = ..()
+ if(!length(accepted_anomalies))
+ return
+ if(core)
+ . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...")
+ else
+ var/list/core_list = list()
+ for(var/path in accepted_anomalies)
+ var/atom/core_path = path
+ core_list += initial(core_path.name)
+ . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
+
+/obj/item/mod/module/anomaly_locked/on_select()
+ if(!core)
+ balloon_alert(mod.wearer, "no core!")
+ return
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/on_process(delta_time)
+ . = ..()
+ if(!core)
+ return FALSE
+
+/obj/item/mod/module/anomaly_locked/on_active_process(delta_time)
+ if(!core)
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params)
+ if(item.type in accepted_anomalies)
+ if(core)
+ balloon_alert(user, "core already in!")
+ return
+ if(!user.transferItemToLoc(item, src))
+ return
+ core = item
+ balloon_alert(user, "core installed")
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ update_icon_state()
+ else
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!core)
+ balloon_alert(user, "no core!")
+ return
+ balloon_alert(user, "removing core...")
+ if(!do_after(user, 3 SECONDS, target = src))
+ balloon_alert(user, "interrupted!")
+ return
+ balloon_alert(user, "core removed")
+ core.forceMove(drop_location())
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(core)
+ core = null
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/update_icon_state()
+ icon_state = initial(icon_state) + (core ? "-core" : "")
+ return ..()
diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm
new file mode 100644
index 00000000000..b567ab57203
--- /dev/null
+++ b/code/modules/mod/modules/module_kinesis.dm
@@ -0,0 +1,250 @@
+///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. \
+ Oddly enough, it doesn't seem to work on living creatures."
+ 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)
+ var/grab_range = 5
+ var/hit_cooldown_time = 1 SECONDS
+ var/movement_animation
+ var/atom/movable/grabbed_atom
+ var/datum/beam/kinesis_beam
+ var/mutable_appearance/kinesis_icon
+ var/atom/movable/screen/fullscreen/kinesis/kinesis_catcher
+ var/datum/looping_sound/gravgen/kinesis/soundloop
+ COOLDOWN_DECLARE(hit_cooldown)
+
+/obj/item/mod/module/anomaly_locked/kinesis/Initialize(mapload)
+ . = ..()
+ soundloop = new(src)
+
+/obj/item/mod/module/anomaly_locked/kinesis/Destroy()
+ if(grabbed_atom)
+ kinesis_catcher = null
+ mod.wearer.clear_fullscreen("kinesis")
+ grabbed_atom.cut_overlay(kinesis_icon)
+ QDEL_NULL(kinesis_beam)
+ grabbed_atom.animate_movement = movement_animation
+ QDEL_NULL(soundloop)
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/kinesis/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(grabbed_atom)
+ launch()
+ clear_grab(playsound = FALSE)
+ return
+ if(!range_check(target))
+ balloon_alert(mod.wearer, "too far!")
+ return
+ if(!can_grab(target))
+ balloon_alert(mod.wearer, "can't grab!")
+ return
+ drain_power(use_power_cost)
+ grabbed_atom = target
+ playsound(grabbed_atom, 'sound/effects/contractorbatonhit.ogg', 75, TRUE)
+ START_PROCESSING(SSfastprocess, src)
+ kinesis_icon = mutable_appearance(icon='icons/effects/effects.dmi', icon_state="kinesis", layer=grabbed_atom.layer-0.1)
+ kinesis_icon.appearance_flags = RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
+ grabbed_atom.add_overlay(kinesis_icon)
+ kinesis_beam = mod.wearer.Beam(grabbed_atom, "kinesis")
+ kinesis_catcher = mod.wearer.overlay_fullscreen("kinesis", /atom/movable/screen/fullscreen/kinesis, 0)
+ kinesis_catcher.kinesis_user = mod.wearer
+ kinesis_catcher.RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, /atom/movable/screen/fullscreen/kinesis.proc/on_move)
+ soundloop.start()
+
+/obj/item/mod/module/anomaly_locked/kinesis/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ clear_grab()
+
+/obj/item/mod/module/anomaly_locked/kinesis/process(delta_time)
+ if(!mod.wearer.client || mod.wearer.incapacitated(ignore_grab = TRUE))
+ clear_grab()
+ return
+ if(!range_check(grabbed_atom))
+ balloon_alert(mod.wearer, "out of range!")
+ clear_grab()
+ return
+ if(!kinesis_catcher.given_turf)
+ return
+ drain_power(use_power_cost/10)
+ mod.wearer.setDir(get_dir(mod.wearer, grabbed_atom))
+ grabbed_atom.set_glide_size()
+ 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 = grabbed_atom.base_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = grabbed_atom.base_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
+ kinesis_beam.redrawing()
+ return
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = grabbed_atom.base_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = grabbed_atom.base_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
+ kinesis_beam.redrawing()
+ var/turf/next_turf = get_step_towards(grabbed_atom, kinesis_catcher.given_turf)
+ if(grabbed_atom.Move(next_turf))
+ if(isitem(grabbed_atom) && (mod.wearer in next_turf))
+ var/obj/item/grabbed_item = grabbed_atom
+ grabbed_item.pickup(mod.wearer)
+ mod.wearer.put_in_hands(grabbed_item)
+ return
+ var/pixel_x_change
+ var/pixel_y_change
+ 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 = grabbed_atom.base_pixel_x + pixel_x_change, pixel_y = grabbed_atom.base_pixel_y + pixel_y_change)
+ kinesis_beam.redrawing()
+ 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(!ismovable(target))
+ return FALSE
+ if(iseffect(target))
+ return FALSE
+ var/atom/movable/movable_target = target
+ if(movable_target.anchored)
+ 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 != DEAD)
+ 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.item_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(kinesis_beam)
+ if(!isitem(grabbed_atom))
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = grabbed_atom.base_pixel_x, pixel_y = grabbed_atom.base_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/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(!(isstructure(source) || ismachinery(source) || isvehicle(source)))
+ return
+ var/damage_self = TRUE
+ var/damage = 8
+ if(source.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(hit_atom.uses_integrity)
+ hit_atom.take_damage(damage, BRUTE, MELEE)
+ if(damage_self && source.uses_integrity)
+ source.take_damage(source.max_integrity/5, BRUTE, MELEE)
+
+/obj/item/mod/module/anomaly_locked/kinesis/prebuilt
+ prebuilt = TRUE
+
+/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype
+ name = "MOD prototype kinesis module"
+ complexity = 0
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ removable = FALSE
+
+/atom/movable/screen/fullscreen/kinesis
+ icon_state = "kinesis"
+ plane = HUD_PLANE
+ mouse_opacity = MOUSE_OPACITY_ICON
+ var/mob/kinesis_user
+ var/given_x = 16
+ var/given_y = 16
+ var/turf/given_turf
+ COOLDOWN_DECLARE(coordinate_cooldown)
+
+/atom/movable/screen/fullscreen/kinesis/proc/on_move(atom/source, atom/oldloc, dir, forced)
+ if(given_turf)
+ var/x_offset = source.loc.x - oldloc.x
+ var/y_offset = source.loc.y - oldloc.y
+ given_turf = locate(given_turf.x+x_offset, given_turf.y+y_offset, given_turf.z)
+
+/atom/movable/screen/fullscreen/kinesis/MouseEntered(location, control, params)
+ . = ..()
+ MouseMove(location, control, params)
+
+/atom/movable/screen/fullscreen/kinesis/MouseMove(location, control, params)
+ if(!kinesis_user?.client || usr != kinesis_user)
+ return
+ if(!COOLDOWN_FINISHED(src, coordinate_cooldown))
+ return
+ COOLDOWN_START(src, coordinate_cooldown, 0.2 SECONDS)
+ var/list/modifiers = params2list(params)
+ var/icon_x = text2num(LAZYACCESS(modifiers, ICON_X))
+ var/icon_y = text2num(LAZYACCESS(modifiers, ICON_Y))
+ var/list/view = getviewsize(kinesis_user.client.view)
+ icon_x *= view[1]/FULLSCREEN_OVERLAY_RESOLUTION_X
+ icon_y *= view[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y
+ var/our_x = round(icon_x / world.icon_size)
+ var/our_y = round(icon_y / world.icon_size)
+ var/mob_x = kinesis_user.x
+ var/mob_y = kinesis_user.y
+ var/mob_z = kinesis_user.z
+ given_turf = locate(mob_x+our_x-round(view[1]/2),mob_y+our_y-round(view[2]/2),mob_z)
+ given_x = round(icon_x - world.icon_size * our_x)
+ given_y = round(icon_y - world.icon_size * our_y)
diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm
new file mode 100644
index 00000000000..f37ee7e1b8e
--- /dev/null
+++ b/code/modules/mod/modules/module_pathfinder.dm
@@ -0,0 +1,168 @@
+///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 Nakamura Engineering, 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, \
+ 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 back at any time. \
+ The implant is stored in the module and needs to be injected in a human to function. \
+ Nakamura Engineering swears up and down there's airbrakes."
+ icon_state = "pathfinder"
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 10
+ 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_notice("Use it on a human to implant them.")
+ else
+ . += span_warning("The implant is missing.")
+
+/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))
+ balloon_alert(user, "interrupted!")
+ return
+ if(!implant.implant(target, user))
+ balloon_alert(user, "can't implant!")
+ return
+ if(target == user)
+ to_chat(user, span_notice("You implant yourself with [implant]."))
+ else
+ target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant]."))
+ playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
+ icon_state = "pathfinder_empty"
+ implant = null
+
+/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/human_user = user
+ if(human_user.get_item_by_slot(mod.slot_flags) && !human_user.dropItemToGround(human_user.get_item_by_slot(mod.slot_flags)))
+ return
+ if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
+ return
+ mod.quick_deploy(user)
+ human_user.update_action_buttons(TRUE)
+ balloon_alert(human_user, "[mod] attached")
+ 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."
+ 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
+
+/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(module?.mod?.ai_controller)
+ end_recall(successful = FALSE)
+ module = null
+ jet_icon = null
+ return ..()
+
+/obj/item/implant/mod/get_data()
+ var/dat = {"Implant Specifications:
+ Name: Nakamura Engineering Pathfinder Implant
+ Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"}
+ return dat
+
+/obj/item/implant/mod/proc/recall()
+ if(!module?.mod)
+ balloon_alert(imp_in, "no connected suit!")
+ return FALSE
+ if(module.mod.open)
+ balloon_alert(imp_in, "suit is open!")
+ return FALSE
+ if(module.mod.ai_controller)
+ balloon_alert(imp_in, "already in transit!")
+ return FALSE
+ if(ismob(get_atom_on_turf(module.mod)))
+ balloon_alert(imp_in, "already on someone!")
+ return FALSE
+ if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE)
+ balloon_alert(imp_in, "too far away!")
+ return FALSE
+ var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
+ module.mod.ai_controller = mod_ai
+ mod_ai.current_movement_target = imp_in
+ mod_ai.blackboard[BB_MOD_TARGET] = imp_in
+ mod_ai.blackboard[BB_MOD_IMPLANT] = src
+ module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
+ module.mod.AddElement(/datum/element/movetype_handler)
+ ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
+ animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
+ module.mod.add_overlay(jet_icon)
+ RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move)
+ balloon_alert(imp_in, "suit recalled")
+ return TRUE
+
+/obj/item/implant/mod/proc/end_recall(successful = TRUE)
+ if(!module?.mod)
+ return
+ QDEL_NULL(module.mod.ai_controller)
+ module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
+ REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
+ module.mod.RemoveElement(/datum/element/movetype_handler)
+ module.mod.cut_overlay(jet_icon)
+ module.mod.transform = matrix()
+ UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
+ if(!successful)
+ balloon_alert(imp_in, "suit lost connection!")
+
+/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
+
+/datum/action/item_action/mod_recall
+ name = "Recall MOD"
+ desc = "Recall a MODsuit anyplace, anytime."
+ check_flags = AB_CHECK_CONSCIOUS
+ background_icon_state = "bg_tech_blue"
+ icon_icon = 'icons/mob/actions/actions_mod.dmi'
+ button_icon_state = "recall"
+ /// The cooldown for the recall.
+ COOLDOWN_DECLARE(recall_cooldown)
+ /// The implant we are linked to.
+ var/obj/item/implant/mod/implant
+
+/datum/action/item_action/mod_recall/New(Target)
+ ..()
+ implant = Target
+
+/datum/action/item_action/mod_recall/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ if(!COOLDOWN_FINISHED(src, recall_cooldown))
+ implant.balloon_alert(implant.imp_in, "on cooldown!")
+ return
+ if(implant.recall())
+ COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
index 4910611c51e..97c788ec70c 100644
--- a/code/modules/mod/modules/modules_engineering.dm
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -204,44 +204,3 @@
return
rcd_scan(src, fade_time = 10 SECONDS)
drain_power(use_power_cost)
-
-///Kinesis - Gives you a special form of telekinesis.
-/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK
- 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. \
- Oddly enough, it doesn't seem to work on living creatures."
- icon_state = "kinesis"
-// module_type = MODULE_ACTIVE
- module_type = MODULE_TOGGLE
-// complexity = 3
- complexity = 0
- active_power_cost = DEFAULT_CHARGE_DRAIN*0.75
-// use_power_cost = DEFAULT_CHARGE_DRAIN*3
- removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/kinesis)
- cooldown_time = 0.5 SECONDS
- overlay_state_inactive = "module_kinesis"
- overlay_state_active = "module_kinesis_on"
- /// Whether the user had TK previously or not.
- var/has_tk = FALSE
-
-/obj/item/mod/module/kinesis/on_activation()
- . = ..()
- if(!.)
- return
- if(mod.wearer.dna.check_mutation(TK))
- has_tk = TRUE
- return
- mod.wearer.dna.add_mutation(TK_MOD)
-
-/obj/item/mod/module/kinesis/on_deactivation()
- . = ..()
- if(!.)
- return
- if(has_tk)
- has_tk = FALSE
- return
- mod.wearer.dna.remove_mutation(TK_MOD)
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 63e6cc3109e..c10193f2180 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -95,11 +95,11 @@
/// Do we give the wearer a speed buff.
var/full_speed = FALSE
/// The ion trail particles left after the jetpack.
- var/datum/effect_system/trail_follow/ion/ion_trail
+ var/datum/effect_system/trail_follow/ion/grav_allowed/ion_trail
/obj/item/mod/module/jetpack/Initialize(mapload)
. = ..()
- ion_trail = new
+ ion_trail = new()
ion_trail.auto_process = FALSE
ion_trail.set_up(src)
@@ -171,6 +171,11 @@
ion_trail.generate_effect()
return TRUE
+/obj/item/mod/module/jetpack/advanced
+ name = "MOD advanced ion jetpack module"
+ icon_state = "jetpack_advanced"
+ full_speed = TRUE
+
///Eating Apparatus - Lets the user eat/drink with the suit on.
/obj/item/mod/module/mouthhole
name = "MOD eating apparatus module"
@@ -379,175 +384,6 @@
/obj/item/mod/module/thermal_regulator/on_active_process(delta_time)
mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time))
-///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 Nakamura Engineering, 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, \
- 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 back at any time. \
- The implant is stored in the module and needs to be injected in a human to function. \
- Nakamura Engineering swears up and down there's airbrakes."
- icon_state = "pathfinder"
- complexity = 2
- use_power_cost = DEFAULT_CHARGE_DRAIN * 10
- 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_notice("Use it on a human to implant them.")
- else
- . += span_warning("The implant is missing.")
-
-/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))
- balloon_alert(user, "interrupted!")
- return
- if(!implant.implant(target, user))
- balloon_alert(user, "can't implant!")
- return
- if(target == user)
- to_chat(user, span_notice("You implant yourself with [implant]."))
- else
- target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant]."))
- playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
- icon_state = "pathfinder_empty"
- implant = null
-
-/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
- if(!ishuman(user))
- return
- var/mob/living/carbon/human/human_user = user
- if(human_user.get_item_by_slot(mod.slot_flags) && !human_user.dropItemToGround(human_user.get_item_by_slot(mod.slot_flags)))
- return
- if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
- return
- mod.quick_deploy(user)
- human_user.update_action_buttons(TRUE)
- balloon_alert(human_user, "[mod] attached")
- 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."
- 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
-
-/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(module?.mod?.ai_controller)
- end_recall(successful = FALSE)
- module = null
- jet_icon = null
- return ..()
-
-/obj/item/implant/mod/get_data()
- var/dat = {"Implant Specifications:
- Name: Nakamura Engineering Pathfinder Implant
- Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"}
- return dat
-
-/obj/item/implant/mod/proc/recall()
- if(!module?.mod)
- balloon_alert(imp_in, "no connected suit!")
- return FALSE
- if(module.mod.open)
- balloon_alert(imp_in, "suit is open!")
- return FALSE
- if(module.mod.ai_controller)
- balloon_alert(imp_in, "already in transit!")
- return FALSE
- if(ismob(get_atom_on_turf(module.mod)))
- balloon_alert(imp_in, "already on someone!")
- return FALSE
- if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE)
- balloon_alert(imp_in, "too far away!")
- return FALSE
- var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
- module.mod.ai_controller = mod_ai
- mod_ai.current_movement_target = imp_in
- mod_ai.blackboard[BB_MOD_TARGET] = imp_in
- mod_ai.blackboard[BB_MOD_IMPLANT] = src
- module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
- module.mod.AddElement(/datum/element/movetype_handler)
- ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
- module.mod.add_overlay(jet_icon)
- RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move)
- balloon_alert(imp_in, "suit recalled")
- return TRUE
-
-/obj/item/implant/mod/proc/end_recall(successful = TRUE)
- if(!module?.mod)
- return
- QDEL_NULL(module.mod.ai_controller)
- module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
- REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- module.mod.RemoveElement(/datum/element/movetype_handler)
- module.mod.cut_overlay(jet_icon)
- module.mod.transform = matrix()
- UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
- if(!successful)
- balloon_alert(imp_in, "suit lost connection!")
-
-/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
-
-/datum/action/item_action/mod_recall
- name = "Recall MOD"
- desc = "Recall a MODsuit anyplace, anytime."
- check_flags = AB_CHECK_CONSCIOUS
- background_icon_state = "bg_tech_blue"
- icon_icon = 'icons/mob/actions/actions_mod.dmi'
- button_icon_state = "recall"
- /// The cooldown for the recall.
- COOLDOWN_DECLARE(recall_cooldown)
- /// The implant we are linked to.
- var/obj/item/implant/mod/implant
-
-/datum/action/item_action/mod_recall/New(Target)
- ..()
- implant = Target
-
-/datum/action/item_action/mod_recall/Trigger(trigger_flags)
- . = ..()
- if(!.)
- return
- if(!COOLDOWN_FINISHED(src, recall_cooldown))
- implant.balloon_alert(implant.imp_in, "on cooldown!")
- return
- if(implant.recall())
- COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
-
///DNA Lock - Prevents people without the set DNA from activating the suit.
/obj/item/mod/module/dna_lock
name = "MOD DNA lock module"
@@ -657,9 +493,9 @@
incompatible_modules = list(/obj/item/mod/module/hat_stabilizer)
/*Intentionally left inheriting 0 complexity and removable = TRUE;
even though it comes inbuilt into the Magnate/Corporate MODS and spawns in maints, I like the idea of stealing them*/
- ///Currently "stored" hat. No armor or function will be inherited, ONLY the icon.
+ /// Currently "stored" hat. No armor or function will be inherited, ONLY the icon.
var/obj/item/clothing/head/attached_hat
- ///Whitelist of attachable hats; read note in Initialize() below this line
+ /// Whitelist of attachable hats, read note in Initialize() below this line
var/static/list/attachable_hats_list
/obj/item/mod/module/hat_stabilizer/Initialize()
@@ -723,7 +559,7 @@
return
if(mod.wearer.transferItemToLoc(hitting_item, src, force = FALSE, silent = TRUE))
attached_hat = hitting_item
- balloon_alert(user, "hat attached, right click to remove")
+ balloon_alert(user, "hat attached, right-click to remove")
mod.wearer.update_inv_back()
/obj/item/mod/module/hat_stabilizer/generate_worn_overlay()
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
index 90fe9ac5ca8..ef6921d4bde 100644
--- a/code/modules/mod/modules/modules_maint.dm
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -52,7 +52,8 @@
playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5)
mod.wearer.client?.give_award(/datum/award/achievement/misc/springlock, mod.wearer)
mod.wearer.apply_damage(500, BRUTE, forced = TRUE, spread_damage = TRUE, sharpness = SHARP_POINTY) //boggers, bogchamp, etc
- mod.wearer.death() //just in case, for some reason, they're still alive
+ if(!HAS_TRAIT(mod.wearer, TRAIT_NODEATH))
+ mod.wearer.death() //just in case, for some reason, they're still alive
flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS)
///Rave Visor - Gives you a rainbow visor and plays jukebox music to you.
diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm
index fa179feca9b..c472a0cd37d 100644
--- a/code/modules/mod/modules/modules_science.dm
+++ b/code/modules/mod/modules/modules_science.dm
@@ -79,11 +79,11 @@
starting_circuit = circuit, \
)
-/*/obj/item/mod/module/circuit/on_install()
- circuit.set_cell(mod.cell)
+/obj/item/mod/module/circuit/on_install()
+ circuit.set_cell(mod.get_cell())
/obj/item/mod/module/circuit/on_uninstall()
- circuit.set_cell(mod.cell)*/
+ circuit.set_cell(mod.get_cell())
/obj/item/mod/module/circuit/on_suit_activation()
circuit.set_on(TRUE)
@@ -160,93 +160,6 @@
return
wearer.set_output(attached_module.mod.wearer)
-///Anomaly Locked - Causes the module to not function without an anomaly.
-/obj/item/mod/module/anomaly_locked
- name = "MOD anomaly locked module"
- desc = "A form of a module, locked behind an anomalous core to function."
- incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
- /// The core item the module runs off.
- var/obj/item/assembly/signaler/anomaly/core
- /// Accepted types of anomaly cores.
- var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
- /// If this one starts with a core in.
- var/prebuilt = FALSE
-
-/obj/item/mod/module/anomaly_locked/Initialize(mapload)
- . = ..()
- if(!prebuilt || !length(accepted_anomalies))
- return
- var/core_path = pick(accepted_anomalies)
- core = new core_path(src)
- update_icon_state()
-
-/obj/item/mod/module/anomaly_locked/Destroy()
- QDEL_NULL(core)
- return ..()
-
-/obj/item/mod/module/anomaly_locked/examine(mob/user)
- . = ..()
- if(!length(accepted_anomalies))
- return
- if(core)
- . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...")
- else
- var/list/core_list = list()
- for(var/path in accepted_anomalies)
- var/atom/core_path = path
- core_list += initial(core_path.name)
- . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
-
-/obj/item/mod/module/anomaly_locked/on_select()
- if(!core)
- balloon_alert(mod.wearer, "no core!")
- return
- return ..()
-
-/obj/item/mod/module/anomaly_locked/on_process(delta_time)
- . = ..()
- if(!core)
- return FALSE
-
-/obj/item/mod/module/anomaly_locked/on_active_process(delta_time)
- if(!core)
- return FALSE
- return TRUE
-
-/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params)
- if(item.type in accepted_anomalies)
- if(core)
- balloon_alert(user, "core already in!")
- return
- if(!user.transferItemToLoc(item, src))
- return
- core = item
- balloon_alert(user, "core installed")
- playsound(src, 'sound/machines/click.ogg', 30, TRUE)
- update_icon_state()
- else
- return ..()
-
-/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
- . = ..()
- if(!core)
- balloon_alert(user, "no core!")
- return
- balloon_alert(user, "removing core...")
- if(!do_after(user, 3 SECONDS, target = src))
- balloon_alert(user, "interrupted!")
- return
- balloon_alert(user, "core removed")
- core.forceMove(drop_location())
- if(Adjacent(user) && !issilicon(user))
- user.put_in_hands(core)
- core = null
- update_icon_state()
-
-/obj/item/mod/module/anomaly_locked/update_icon_state()
- icon_state = initial(icon_state) + (core ? "-core" : "")
- return ..()
-
///Anti-Gravity - Makes the user weightless.
/obj/item/mod/module/anomaly_locked/antigrav
name = "MOD anti-gravity module"
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 7b122b3a1b7..2fd820d22c8 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -75,13 +75,13 @@
return 100*charge/maxcharge
// use power from a cell
-/obj/item/stock_parts/cell/use(amount)
+/obj/item/stock_parts/cell/use(amount, force)
if(rigged && amount > 0)
explode()
return FALSE
- if(charge < amount)
+ if(!force && charge < amount)
return FALSE
- charge = (charge - amount)
+ charge = max(charge - amount, 0)
if(!istype(loc, /obj/machinery/power/apc))
SSblackbox.record_feedback("tally", "cell_used", 1, type)
return TRUE
@@ -213,6 +213,10 @@
/obj/item/stock_parts/cell/get_part_rating()
return rating * maxcharge
+/obj/item/stock_parts/cell/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+ var/obj/item/mod/control/mod = storage_holder
+ return !(istype(mod) && mod.open)
+
/* Cell variants*/
/obj/item/stock_parts/cell/empty/Initialize(mapload)
. = ..()
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 969c1c57c47..a89cae6919c 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1354,22 +1354,29 @@
materials = list(/datum/material/plasma = 1000, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/plasma_stabilizer
+/datum/design/module/mod_glove_translator
+ name = "MOD Module: Glove Translator"
+ id = "mod_sign_radio"
+ materials = list(/datum/material/iron = 750, /datum/material/glass = 500)
+ build_path = /obj/item/mod/module/signlang_radio
+
/datum/design/module/mod_antigrav
name = "MOD Module: Anti-Gravity"
id = "mod_antigrav"
- materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 1000)
+ materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 2000)
build_path = /obj/item/mod/module/anomaly_locked/antigrav
department_type = MODULE_SCIENCE
/datum/design/module/mod_teleporter
name = "MOD Module: Teleporter"
id = "mod_teleporter"
- materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/bluespace = 1000)
+ materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/bluespace = 2000)
build_path = /obj/item/mod/module/anomaly_locked/teleporter
department_type = MODULE_SCIENCE
-/datum/design/module/mod_glove_translator
- name = "MOD Module: Glove Translator"
- id = "mod_sign_radio"
- materials = list(/datum/material/iron = 750, /datum/material/glass = 500)
- build_path = /obj/item/mod/module/signlang_radio
+/datum/design/module/mod_kinesis
+ name = "MOD Module: Kinesis"
+ id = "mod_kinesis"
+ materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 1000, /datum/material/bluespace = 1000)
+ build_path = /obj/item/mod/module/anomaly_locked/kinesis
+ department_type = MODULE_ENGINEERING
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index e1c28040922..832e45c46ad 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -123,13 +123,13 @@
display_name = "Basic Modular Suits"
description = "Specialized back mounted power suits with various different modules."
design_ids = list(
- "mod_armor_standard",
"mod_boots",
"mod_chestplate",
"mod_gauntlets",
"mod_helmet",
"mod_paint_kit",
"mod_shell",
+ "mod_armor_standard",
"mod_storage",
"mod_welding",
"mod_mouthhole",
@@ -1557,7 +1557,6 @@
"mod_clamp",
"mod_drill",
"mod_orebag",
- "mod_pathfinder",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
@@ -1642,7 +1641,17 @@
"mod_antigrav",
"mod_teleporter",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+
+/datum/techweb_node/mod_anomaly_engi
+ id = "mod_anomaly_engi"
+ display_name = "Engineering Anomalock Modular Suits"
+ description = "Advanced modules for modular suits, using anomaly cores to become even better engineers."
+ prereq_ids = list("mod_advanced_engineering", "mod_anomaly")
+ design_ids = list(
+ "mod_kinesis",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
////////////////////////mech technology////////////////////////
/datum/techweb_node/adv_mecha
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 2262b111f40..1e467b8e099 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -903,9 +903,8 @@
qdel(src)
/obj/item/slimepotion/speed/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
- . = ..()
if(!isitem(storage_holder))
- return
+ return TRUE
if(istype(storage_holder, /obj/item/mod/control))
var/obj/item/mod/control/mod = storage_holder
return mod.slowdown_inactive <= 0
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 3efd1ee9214..7ff9807d9e5 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 251c02da7fd..bcf9f980442 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/hud/screen_full.dmi b/icons/hud/screen_full.dmi
index 39d8ff797a0..f54a0bd9444 100644
Binary files a/icons/hud/screen_full.dmi and b/icons/hud/screen_full.dmi differ
diff --git a/icons/mob/clothing/mod.dmi b/icons/mob/clothing/mod.dmi
index d2a95e2366a..7530b8443b6 100644
Binary files a/icons/mob/clothing/mod.dmi and b/icons/mob/clothing/mod.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index bdb4cd72312..546d1bda749 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/machines/mod_installer.dmi b/icons/obj/machines/mod_installer.dmi
index 0b7a6507ec9..ae943157d6c 100644
Binary files a/icons/obj/machines/mod_installer.dmi and b/icons/obj/machines/mod_installer.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index a19130f0cc3..84bed840408 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3347,6 +3347,8 @@
#include "code\modules\mod\mod_types.dm"
#include "code\modules\mod\mod_ui.dm"
#include "code\modules\mod\modules\_module.dm"
+#include "code\modules\mod\modules\module_kinesis.dm"
+#include "code\modules\mod\modules\module_pathfinder.dm"
#include "code\modules\mod\modules\modules_antag.dm"
#include "code\modules\mod\modules\modules_engineering.dm"
#include "code\modules\mod\modules\modules_general.dm"