Files
Bubberstation/code/game/objects/effects/portals.dm
SmArtKar ad111f4950 Spacemove refactor - Newtonian physics (#84869)
## About The Pull Request

This PR significantly enhances how zero-g movement works. Its no longer
locked to one of 8 directions, everything now has inertia and is
affected by weight. This means that throwing a piece of wire will no
longer completely reverse your movement direction, and that being thrown
out of mass driver no longer will slow you down to a halt at some point.

This leads to following gameplay changes:
* Guns now accelerate you. Ballistics have higher acceleration than
lasers, and higher calibers have higher acceleration than smaller ones.
This means that firing full-auto weapons in zero-g will make you drift
and accelerate significantly. While this can be a hilarious way to
travel in space, it makes using them trickier.
* Impacting a wall or an object while moving at high speeds will cause
you to violently crash into it as if you were thrown. Careful when
exploring!
* Jetpacks now have inertia. Changes introduced in #84712 have been
mostly reverted, although speed buff has been reduced to 0.3 instead of
0.5 (although this is compensated by new movement mechanics, so overall
speed should be roughly equal). All MODsuit jetpacks now possess the
speed boost. Advanced MODsuit jets (which has also been added back) and
captain's jetpack instead have higher acceleration and stabilization
power, providing much more precise control over your movement.
* Firing guns while moving on a jetpack will partially negate your
pack's acceleration, slowing you down. Non-advanced jetpacks'
stabilization is not enough to compensate for heavy caliber weaponry as
sniper rifles, shotguns or rocket launchers.
* You no longer instantly decelerate upon sliding along a wall. Instead,
it may take a few tiles if you are moving at extreme speeds. Passing
over lattices still allows you to grab onto them!

As space movement is angle-based instead of dir-based now, its much more
smooth than before due to using new movement logic.

Example of jetpack stabilization in action:

https://github.com/tgstation/tgstation/assets/44720187/6761a4fd-b7de-4523-97ea-38144b8aab41

And, of course, you can do this now.

![jetpack_500](https://github.com/tgstation/tgstation/assets/44720187/37b11cd8-2bd1-4640-ae0c-5e0cc505bf52)

**This pull request requires extensive gameplay testing before
merging**, as a large amount of numbers have been picked arbitrarily in
an attempt to keep consistency with previous behavior (guns and
normal-sized items applying 1 drift force, which is equal to what
everything applied before this PR). Jetpacks and impacts may also
require adjustments as to not be frustrating to use.

Closes #85165

## Why It's Good For The Game

Zero-G refactor - currently our zero-g movement is rather ugly and can
be uncomfortable to work with. A piece of cable being able to accelerate
you the same as a duffelbag full of items when thrown makes no sense,
and so does instantly changing directions. Inertia-based version is
smoother and more intuitive. This also makes being thrown into space
more of a hazard (possibly opening the door for explosive
decompressions?)
Jetpack inertia and gun changes - this is mostly a consequence of
inertia-based movement. However, zero-g combat being preferred during
modes like warops was an issue due to it negatively affecting everyone
without jetpacks which are in limited supply onboard. This reverts the
mobility changes which severely impacted space exploration, while making
zero-g combat more dangerous and having it require more skill to be a
viable option.

## What's left

- [x] Refactor moth wings to use jetpack code
- [x] Refactor functional wings to use jetpack code
- [x] Locate and fix a recursion runtime that sometimes occurs upon
splattering against a wall
- [x] Add craftable tethers and modify engineering MOD tethers to use
the same system

## Changelog
🆑
add: You can now craft tether anchors, which can be secured with a
wrench and attached to with right click. They won't let you drift into
space and you can adjust tether length/cut it via lmb/rmb/ctrl click on
the wire.
add: MOD tethers now remotely place and connect to tether anchors
instead of throwing you at where they landed.
balance: MOD tethers can now be used in gravity
balance: Jetpacks are now inertia-based. 
balance: Guns can accelerate you significantly in zero-g.
balance: All jetpacks now give you equal speed buff, however advanced
MOD ion jets and captain's jetpack have higher acceleration/deceleration
values.
refactor: Refactored zero-g movement to be inertia-based and utilize
angles instead of directions.
/🆑
2024-09-26 02:49:54 -07:00

239 lines
7.6 KiB
Plaintext

/proc/create_portal_pair(turf/source, turf/destination, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal)
if(!istype(source) || !istype(destination))
return
var/turf/actual_destination = get_teleport_turf(destination, accuracy)
var/obj/effect/portal/P1 = new newtype(source, _lifespan, null, FALSE, null)
var/obj/effect/portal/P2 = new newtype(actual_destination, _lifespan, P1, TRUE, null)
if(!istype(P1) || !istype(P2))
return
playsound(P1, SFX_PORTAL_CREATED, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
playsound(P2, SFX_PORTAL_CREATED, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
P1.link_portal(P2)
P1.hardlinked = TRUE
return list(P1, P2)
/obj/effect/portal
name = "portal"
desc = "Looks unstable. Best to test it with the clown."
icon = 'icons/obj/anomaly.dmi'
icon_state = "portal"
anchored = TRUE
density = TRUE // dense for receiving bumbs
layer = HIGH_OBJ_LAYER
light_system = COMPLEX_LIGHT
light_range = 3
light_power = 1
light_on = TRUE
light_color = COLOR_BLUE_LIGHT
/// Are mechs able to enter this portal?
var/mech_sized = FALSE
/// A reference to another "linked" destination portal
var/obj/effect/portal/linked
/// Requires a linked portal at all times. Destroy if there's no linked portal, if there is destroy it when this one is deleted.
var/hardlinked = TRUE
/// What teleport channel does this portal use?
var/teleport_channel = TELEPORT_CHANNEL_BLUESPACE
/// For when a portal needs a hard target and isn't to be linked.
var/turf/hard_target
/// Do we teleport anchored objects?
var/allow_anchored = FALSE
/// What precision value do we pass to do_teleport (how far from the target destination we will pop out at).
var/innate_accuracy_penalty = 0
/// Used to track how often sparks should be output. Might want to turn this into a cooldown.
var/last_effect = 0
/// Does this portal bypass teleport restrictions? like TRAIT_NO_TELEPORT and NOTELEPORT flags.
var/force_teleport = FALSE
/// Does this portal create spark effect when teleporting?
var/sparkless = TRUE
/// If FALSE, the wibble filter will not be applied to this portal (only a visual effect).
var/wibbles = TRUE
/obj/effect/portal/anom
name = "wormhole"
icon = 'icons/obj/anomaly.dmi'
icon_state = "anom"
layer = RIPPLE_LAYER
plane = ABOVE_GAME_PLANE
mech_sized = TRUE
teleport_channel = TELEPORT_CHANNEL_WORMHOLE
light_on = FALSE
wibbles = FALSE
/obj/effect/portal/Move(newloc)
for(var/T in newloc)
if(istype(T, /obj/effect/portal))
return FALSE
return ..()
// Prevents portals spawned by jaunter/handtele from floating into space when relocated to an adjacent tile.
/obj/effect/portal/newtonian_move(inertia_angle, instant = FALSE, start_delay = 0, drift_force = 0, controlled_cap = null)
return TRUE
/obj/effect/portal/attackby(obj/item/W, mob/user, params)
if(user && Adjacent(user))
teleport(user)
return TRUE
/obj/effect/portal/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(HAS_TRAIT(mover, TRAIT_NO_TELEPORT) && !force_teleport)
return TRUE
/obj/effect/portal/Bumped(atom/movable/bumper)
teleport(bumper)
/obj/effect/portal/attack_hand(mob/user, list/modifiers)
. = ..()
if(.)
return
if(Adjacent(user))
teleport(user)
/obj/effect/portal/attack_robot(mob/living/user)
if(Adjacent(user))
teleport(user)
/obj/effect/portal/Initialize(mapload, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override)
. = ..()
GLOB.portals += src
if(!istype(_linked) && automatic_link)
. = INITIALIZE_HINT_QDEL
CRASH("Somebody fucked up.")
if(_lifespan > 0)
addtimer(CALLBACK(src, PROC_REF(expire)), _lifespan, TIMER_DELETE_ME)
link_portal(_linked)
hardlinked = automatic_link
if(isturf(hard_target_override))
hard_target = hard_target_override
if(wibbles)
apply_wibbly_filters(src)
/obj/effect/portal/proc/expire()
playsound(loc, SFX_PORTAL_CLOSE, 50, FALSE, SHORT_RANGE_SOUND_EXTRARANGE)
qdel(src)
/obj/effect/portal/singularity_pull()
return
/obj/effect/portal/singularity_act()
return
/obj/effect/portal/proc/link_portal(obj/effect/portal/newlink)
linked = newlink
/obj/effect/portal/Destroy()
GLOB.portals -= src
if(hardlinked && !QDELETED(linked))
QDEL_NULL(linked)
else
linked = null
return ..()
/obj/effect/portal/attack_ghost(mob/dead/observer/O)
if(!teleport(O, TRUE))
return ..()
/obj/effect/portal/proc/teleport(atom/movable/M, force = FALSE)
if(!force && (!istype(M) || iseffect(M) || (ismecha(M) && !mech_sized) || (!isobj(M) && !ismob(M)))) //Things that shouldn't teleport.
return
var/turf/real_target = get_link_target_turf()
if(!istype(real_target))
return FALSE
if(!force && (!ismecha(M) && !isprojectile(M) && M.anchored && !allow_anchored))
return
var/no_effect = FALSE
if(last_effect == world.time || sparkless)
no_effect = TRUE
else
last_effect = world.time
var/turf/start_turf = get_turf(M)
if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport))
if(isprojectile(M))
var/obj/projectile/P = M
P.ignore_source_check = TRUE
new /obj/effect/temp_visual/portal_animation(start_turf, src, M)
playsound(start_turf, SFX_PORTAL_ENTER, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
playsound(real_target, SFX_PORTAL_ENTER, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
return FALSE
/obj/effect/portal/proc/get_link_target_turf()
var/turf/real_target
if(!istype(linked) || QDELETED(linked))
if(hardlinked)
qdel(src)
if(!istype(hard_target) || QDELETED(hard_target))
hard_target = null
return
else
real_target = hard_target
linked = null
else
real_target = get_turf(linked)
return real_target
/obj/effect/portal/permanent
name = "permanent portal"
desc = "An unwavering portal that will never fade."
hardlinked = FALSE // dont qdel my portal nerd
force_teleport = TRUE // force teleports because they're a mapmaker tool
var/id // var edit or set id in map editor
/obj/effect/portal/permanent/proc/set_linked()
if(!id)
return
for(var/obj/effect/portal/permanent/P in GLOB.portals - src)
if(P.id == id)
P.linked = src
linked = P
break
/obj/effect/portal/permanent/teleport(atom/movable/M, force = FALSE)
set_linked() // update portal links
. = ..()
/obj/effect/portal/permanent/one_way // doesn't have a return portal, can have multiple exits, /obj/effect/landmark/portal_exit to mark them
name = "one-way portal"
desc = "You get the feeling that this might not be the safest thing you've ever done."
/obj/effect/portal/permanent/one_way/set_linked()
if(!id)
return
var/list/possible_turfs = list()
for(var/obj/effect/landmark/portal_exit/PE in GLOB.landmarks_list)
if(PE.id == id)
var/turf/T = get_turf(PE)
if(T)
possible_turfs |= T
if(possible_turfs.len)
hard_target = pick(possible_turfs)
/obj/effect/portal/permanent/one_way/one_use
name = "one-use portal"
desc = "This is probably the worst decision you'll ever make in your life."
/obj/effect/portal/permanent/one_way/one_use/teleport(atom/movable/M, force = FALSE)
. = ..()
if (. && !isdead(M))
expire()
/**
* Animation used for transitioning atoms which are teleporting somewhere via a portal
*
* To use, pass it the atom doing the teleporting and the atom that is being teleported in init.
*/
/obj/effect/temp_visual/portal_animation
duration = 0.25 SECONDS
/obj/effect/temp_visual/portal_animation/Initialize(mapload, atom/portal, atom/movable/teleporting)
. = ..()
if(isnull(portal) || isnull(teleporting))
return
appearance = teleporting.appearance
dir = teleporting.dir
layer = portal.layer + 0.01
alpha = teleporting.alpha
animate(src, pixel_x = (portal.x * 32) - (x * 32), pixel_y = (portal.y * 32) - (y * 32), alpha = 0, time = duration)