Files
Bubberstation/code/modules/mod/modules/module_kinesis.dm
Fikou f9445e027f makes beams better (plus small kinesis tweaks) (#71768)
## About The Pull Request
kinesis now waits for its first inputs to be given before firing, it
should no longer pull the item to the center of the screen
the kinesis overlay now is emissive
beam emissivity now works (emissive appearances no longer reset
transform)
beams now have a variable whether or not they should be emissive,
because of stuff like vines or chains from the meat hook
fixes https://github.com/tgstation/tgstation/issues/71640

## Why It's Good For The Game
neat visual stuff

## Changelog
🆑
fix: fixes beam glow in the dark effects
fix: kinesis now doesnt pull the item to the centre of the screen when
first used
fix: physical beams (like vines or meat hook chain) no longer glow in
the dark
/🆑
2022-12-09 15:58:58 -05:00

286 lines
11 KiB
Plaintext

///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)
/// Range of the knesis grab.
var/grab_range = 5
/// Time between us hitting objects with kinesis.
var/hit_cooldown_time = 1 SECONDS
/// Stat required for us to grab a mob.
var/stat_required = DEAD
/// How long we stun a mob for.
var/mob_stun_time = 5 SECONDS
/// Atom we grabbed with kinesis.
var/atom/movable/grabbed_atom
/// Ref of the beam following the grabbed atom.
var/datum/beam/kinesis_beam
/// Overlay we add to each grabbed atom.
var/mutable_appearance/kinesis_icon
/// Our mouse movement catcher.
var/atom/movable/screen/fullscreen/kinesis/kinesis_catcher
/// The sounds playing while we grabbed an object.
var/datum/looping_sound/gravgen/kinesis/soundloop
/// The cooldown between us hitting objects with kinesis.
COOLDOWN_DECLARE(hit_cooldown)
/obj/item/mod/module/anomaly_locked/kinesis/Initialize(mapload)
. = ..()
soundloop = new(src)
/obj/item/mod/module/anomaly_locked/kinesis/Destroy()
QDEL_NULL(soundloop)
return ..()
/obj/item/mod/module/anomaly_locked/kinesis/on_select_use(atom/target)
. = ..()
if(!.)
return
if(!mod.wearer.client)
return
if(grabbed_atom)
launch()
clear_grab(playsound = FALSE)
return
if(!range_check(target))
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
if(isliving(grabbed_atom))
var/mob/living/grabbed_mob = grabbed_atom
grabbed_mob.Stun(mob_stun_time)
playsound(grabbed_atom, 'sound/effects/contractorbatonhit.ogg', 75, TRUE)
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
kinesis_icon.overlays += emissive_appearance(icon = 'icons/effects/effects.dmi', icon_state = "kinesis", offset_spokesman = grabbed_atom)
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.view_list = getviewsize(mod.wearer.client.view)
kinesis_catcher.RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/atom/movable/screen/fullscreen/kinesis, on_move))
kinesis_catcher.RegisterSignal(mod.wearer, COMSIG_VIEWDATA_UPDATE, TYPE_PROC_REF(/atom/movable/screen/fullscreen/kinesis, on_viewdata_update))
kinesis_catcher.calculate_params()
soundloop.start()
START_PROCESSING(SSfastprocess, src)
/obj/item/mod/module/anomaly_locked/kinesis/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
clear_grab(playsound = !deleting)
/obj/item/mod/module/anomaly_locked/kinesis/process(delta_time)
if(!mod.wearer.client || mod.wearer.incapacitated(IGNORE_GRAB))
clear_grab()
return
if(!range_check(grabbed_atom))
balloon_alert(mod.wearer, "out of range!")
clear_grab()
return
drain_power(use_power_cost/10)
if(kinesis_catcher.mouse_params)
kinesis_catcher.calculate_params()
if(!kinesis_catcher.given_turf)
return
mod.wearer.setDir(get_dir(mod.wearer, grabbed_atom))
if(grabbed_atom.loc == kinesis_catcher.given_turf)
if(grabbed_atom.pixel_x == kinesis_catcher.given_x - world.icon_size/2 && grabbed_atom.pixel_y == kinesis_catcher.given_y - world.icon_size/2)
return //spare us redrawing if we are standing still
animate(grabbed_atom, 0.2 SECONDS, pixel_x = 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, get_dir(grabbed_atom, next_turf), 8))
if(isitem(grabbed_atom) && (mod.wearer in next_turf))
var/obj/item/grabbed_item = grabbed_atom
clear_grab()
grabbed_item.pickup(mod.wearer)
mod.wearer.put_in_hands(grabbed_item)
return
var/pixel_x_change = 0
var/pixel_y_change = 0
var/direction = get_dir(grabbed_atom, next_turf)
if(direction & NORTH)
pixel_y_change = world.icon_size/2
else if(direction & SOUTH)
pixel_y_change = -world.icon_size/2
if(direction & EAST)
pixel_x_change = world.icon_size/2
else if(direction & WEST)
pixel_x_change = -world.icon_size/2
animate(grabbed_atom, 0.2 SECONDS, pixel_x = 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(mod.wearer == target)
return FALSE
if(!ismovable(target))
return FALSE
if(iseffect(target))
return FALSE
var/atom/movable/movable_target = target
if(movable_target.anchored)
return FALSE
if(movable_target.throwing)
return FALSE
if(movable_target.move_resist >= MOVE_FORCE_OVERPOWERING)
return FALSE
if(ismob(movable_target))
if(!isliving(movable_target))
return FALSE
var/mob/living/living_target = movable_target
if(living_target.stat < stat_required)
return FALSE
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_REF(launch_impact))
var/turf/target_turf = get_turf_in_angle(get_angle(mod.wearer, grabbed_atom), get_turf(src), 10)
grabbed_atom.throw_at(target_turf, range = grab_range, speed = grabbed_atom.density ? 3 : 4, thrower = mod.wearer, spin = isitem(grabbed_atom))
/obj/item/mod/module/anomaly_locked/kinesis/proc/launch_impact(atom/movable/source, atom/hit_atom, datum/thrownthing/thrownthing)
UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
if(!(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/list/view_list
var/given_x
var/given_y
var/turf/given_turf
var/mouse_params
/atom/movable/screen/fullscreen/kinesis/proc/on_move(atom/source, atom/oldloc, dir, forced)
SIGNAL_HANDLER
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/proc/on_viewdata_update(datum/source, view)
SIGNAL_HANDLER
view_list = getviewsize(view)
/atom/movable/screen/fullscreen/kinesis/MouseEntered(location, control, params)
. = ..()
MouseMove(location, control, params)
if(usr == kinesis_user)
calculate_params()
/atom/movable/screen/fullscreen/kinesis/MouseMove(location, control, params)
if(usr != kinesis_user)
return
mouse_params = params
/atom/movable/screen/fullscreen/kinesis/proc/calculate_params()
var/list/modifiers = params2list(mouse_params)
var/icon_x = text2num(LAZYACCESS(modifiers, VIS_X))
var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y))
var/our_x = round(icon_x / world.icon_size)
var/our_y = round(icon_y / world.icon_size)
given_turf = locate(kinesis_user.x+our_x-round(view_list[1]/2),kinesis_user.y+our_y-round(view_list[2]/2),kinesis_user.z)
given_x = round(icon_x - world.icon_size * our_x, 1)
given_y = round(icon_y - world.icon_size * our_y, 1)
/obj/item/mod/module/anomaly_locked/kinesis/plus
name = "MOD kinesis+ module"
desc = "A modular plug-in to the forearm, this module was recently redeveloped in secret. \
The bane of all ne'er-do-wells, the kinesis+ module is a powerful tool that allows the user \
to manipulate the world around them. Like it's older counterpart, it's capable of manipulating \
structures, machinery, vehicles, and, thanks to the fruitful efforts of it's creators - living \
beings. They can, however, still struggle after an initial burst of inertia."
complexity = 0
prebuilt = TRUE
stat_required = CONSCIOUS