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"