Kinesis Module (#64151)

fixes #64029 (Springlock MOD module kills you even when you have Memento Mori necklace)
fixes #64136 (modsuits cant run out of charge)
fixes #64158 (trying to install a battery into a modsuit with free storage space places the battery into storage)
fixes #64186 (ModSuits : taking out cell doesn't give it back)
fixes #64161 (Modsuit cores disappear in construction)
makes mod jetpacks show particles indoors
gives the prototype suit back their upgraded cell (they literally lose 1% of power per 3 seconds with their current one)
Fully reworks the kinesis module to not be TK but lamer, it is now a gravitational anomaly locked module. 

Unique stuff that can be used in fun scenarios
This commit is contained in:
Fikou
2022-01-18 16:30:11 -08:00
committed by GitHub
parent 2d48341d80
commit 505e441495
31 changed files with 602 additions and 348 deletions
+1
View File
@@ -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
+1 -1
View File
@@ -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 ..()
+4 -4
View File
@@ -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)
+1 -3
View File
@@ -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,
+6 -4
View File
@@ -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,
)
+90 -1
View File
@@ -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
@@ -288,3 +290,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 <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)
. += 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 ..()
+250
View File
@@ -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)
@@ -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 = {"<b>Implant Specifications:</b><BR>
<b>Name:</b> Nakamura Engineering Pathfinder Implant<BR>
<b>Implant Details:</b> Allows for the recall of a Modular Outerwear Device by the implant owner at any time.<BR>"}
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)
@@ -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)
+10 -174
View File
@@ -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 = {"<b>Implant Specifications:</b><BR>
<b>Name:</b> Nakamura Engineering Pathfinder Implant<BR>
<b>Implant Details:</b> Allows for the recall of a Modular Outerwear Device by the implant owner at any time.<BR>"}
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()
+2 -1
View File
@@ -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.
+3 -90
View File
@@ -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 <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)
. += 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"