Fix energy ball movement, update arc path (#29812)

* Fix and update energy ball movement.

* More tesla fixes.
This commit is contained in:
Charlie Nolan
2025-08-07 16:58:22 +00:00
committed by GitHub
parent 001430f6fc
commit df025c7340
5 changed files with 114 additions and 88 deletions
@@ -19,12 +19,14 @@
COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered)
)
AddElement(/datum/element/connect_loc, loc_connections)
GLOB.tesla_containment += src
/obj/machinery/field/containment/Destroy()
if(FG1)// These checks are mostly in case a field is spawned in by accident.
FG1.fields -= src
if(FG2)
FG2.fields -= src
GLOB.tesla_containment -= src
return ..()
/obj/machinery/field/containment/attack_hand(mob/user)
@@ -18,8 +18,6 @@ field_generator energy level display
#define FG_CHARGING 1
#define FG_ONLINE 2
GLOBAL_LIST_EMPTY(field_generator_fields)
/obj/machinery/field/generator
name = "Field Generator"
desc = "A large thermal battery that projects a high amount of energy when powered."
@@ -335,7 +333,6 @@ GLOBAL_LIST_EMPTY(field_generator_fields)
CF.dir = field_dir
fields += CF
G.fields += CF
GLOB.field_generator_fields += CF
for(var/mob/living/L in T)
CF.Crossed(L, null)
@@ -348,7 +345,6 @@ GLOBAL_LIST_EMPTY(field_generator_fields)
clean_up = TRUE
for(var/F in fields)
qdel(F)
GLOB.field_generator_fields -= F
for(var/CG in connected_gens)
var/obj/machinery/field/generator/FG = CG
+105 -84
View File
@@ -31,9 +31,9 @@
var/energy_to_lower = -20
var/obj/singularity/energy_ball/parent_energy_ball
/// Turf where the tesla will move to if it's loose
var/turf/target_turf
/// Direction we have to go to go towards the target turf
var/movement_dir
var/turf/movement_goal
/// The next spot the tesla will zoom to and stop at on its way to movement_goal
var/turf/move_target
/// Variable that defines whether it has a field generator close enough
var/has_close_field = FALSE
/// How many tiles do we move per movement step?
@@ -75,6 +75,9 @@
GLOB.poi_list -= src
QDEL_LIST_CONTENTS(orbiting_balls)
movement_goal = null
move_target = null
GLOB.move_manager.stop_looping(src)
return ..()
/obj/singularity/energy_ball/admin_investigate_setup()
@@ -110,112 +113,135 @@
. += "There are [length(orbiting_balls)] mini-balls orbiting it."
/obj/singularity/energy_ball/proc/move_the_basket_ball()
if(!loc)
// Don't move while nowhere.
movement_goal = null
move_target = null
GLOB.move_manager.stop_looping(src)
return
has_close_field = FALSE
for(var/i in 1 to length(GLOB.field_generator_fields))
var/temp_distance = get_dist(src, GLOB.field_generator_fields[i])
for(var/i in 1 to length(GLOB.tesla_containment))
var/temp_distance = get_dist(src, GLOB.tesla_containment[i])
if(temp_distance <= 15)
has_close_field = TRUE
break
if(has_close_field)
// We're in range of a containment field. Stop long-distance movement.
movement_goal = null
move_target = null
GLOB.move_manager.stop_looping(src)
var/turf/T = get_step(src, pick(GLOB.alldirs))
if(can_move(T))
forceMove(T)
for(var/mob/living/carbon/C in loc)
dust_mobs(C)
return
if(!target_turf || loc == target_turf)
if(loc == movement_goal)
// When reaching our target, clear it out, but wait for a moment before picking a new one.
movement_goal = null
return
if(!movement_goal)
// If we don't have a target, pick one.
find_the_basket()
move_target = null
if(loc != move_target && !isnull(move_target))
return
var/turf/move_target = next_move_target()
// Pick a short-term goal.
move_target = next_move_target()
if(move_target)
// Fire an energy beam at it, then start moving.
INVOKE_ASYNC(src, PROC_REF(arc_and_move))
/obj/singularity/energy_ball/proc/next_move_target()
if(!isturf(loc) || !isturf(movement_goal))
return null
var/x_diff = movement_goal.x - x
var/y_diff = movement_goal.y - y
if(abs(x_diff) <= steps_per_move && abs(y_diff) <= steps_per_move)
// We're close, go there directly.
return movement_goal
var/target_x
var/target_y
if(abs(x_diff) >= abs(y_diff))
// 8 tiles along X.
target_x = x + 8 * sign(x_diff)
// Scale back Y movement to match the difference in distance along the axes.
target_y = y + 8 * y_diff / abs(x_diff)
else
// 8 tiles along Y.
target_y = y + 8 * sign(y_diff)
// Scale back X movement to match the difference in distance along the axes.
target_x = x + 8 * x_diff / abs(y_diff)
return locate(target_x, target_y, z)
/obj/singularity/energy_ball/proc/arc_and_move()
if(!loc || !move_target)
// We went to nowhere before the INVOKE_ASYNC resolved. Abort, abort!
return
// Initial beam.
movement_beam(move_target, 1.5 SECONDS)
sleep(0.5 SECONDS)
// MORE POWER
// MORE POWER!
movement_beam(move_target, 1 SECONDS)
sleep(0.5 SECONDS)
GLOB.move_manager.home_onto(src, move_target, 0, 10)
// Follow that arc!
GLOB.move_manager.stop_looping(src)
GLOB.move_manager.move_towards(src, move_target, 0.5, 10)
/obj/singularity/energy_ball/proc/on_atom_entered(datum/source, atom/movable/entered)
var/mob/living/living_entered = entered
if(istype(living_entered))
living_entered.dust()
/datum/move_with_corner
var/turf/start
var/turf/end
var/turf/corner
var/pre_corner_x_unit
var/pre_corner_y_unit
var/pre_corner_dist
var/post_corner_x_unit
var/post_corner_y_unit
var/post_corner_dist
/datum/move_with_corner/New(atom/start_in, atom/end_in)
start = start_in
end = end_in
pre_corner_x_unit = (end.x > start.x) ? 1 : -1
pre_corner_y_unit = (end.y > start.y) ? 1 : -1
if(start.x == end.x)
// Moving along y
corner = end
pre_corner_x_unit = 0
pre_corner_dist = abs(start.y - end.y)
return
if(start.y == end.y)
// Moving along x
corner = end
pre_corner_y_unit = 0
pre_corner_dist = abs(start.x - end.x)
return
pre_corner_dist = min(abs(start.x-end.x), abs(start.y-end.y))
corner = locate(start.x + pre_corner_x_unit * pre_corner_dist, start.y + pre_corner_y_unit * pre_corner_dist, start.z)
if(start.x + pre_corner_x_unit * pre_corner_dist == end.x)
// x finished first, move along y
post_corner_x_unit = 0
post_corner_dist = abs(start.y - end.y) - pre_corner_dist
else
// y finished first, move along x
post_corner_y_unit = 0
post_corner_dist = abs(start.x - end.x) - pre_corner_dist
/datum/move_with_corner/proc/get_early_end(dist)
if(dist <= pre_corner_dist)
return locate(start.x + pre_corner_x_unit * dist, start.y + pre_corner_y_unit * dist, start.z)
else if(dist == pre_corner_dist)
return corner
else if(dist >= pre_corner_dist + post_corner_dist)
return end
var/remaining_dist = dist - pre_corner_dist
return locate(corner.x + remaining_dist * post_corner_x_unit, corner.y + remaining_dist * post_corner_y_unit, corner.z)
/obj/singularity/energy_ball/proc/next_move_target()
var/datum/move_with_corner/path = new(loc, target_turf)
return path.get_early_end(steps_per_move)
/obj/singularity/energy_ball/proc/movement_beam(turf/move_target, duration)
var/datum/move_with_corner/path = new(loc, move_target)
loc.Beam(path.corner, "lightning[rand(1, 12)]", 'icons/effects/effects.dmi', duration, INFINITY)
if(path.corner != move_target)
path.corner.Beam(move_target, "lightning[rand(1, 12)]", 'icons/effects/effects.dmi', duration, INFINITY)
loc.Beam(move_target, "lightning[rand(1, 12)]", 'icons/effects/effects.dmi', duration, INFINITY)
/obj/singularity/energy_ball/proc/find_the_basket()
var/area/where_to_move = pick(all_possible_areas) // Grabs a random area that isn't restricted
var/turf/target_area_turfs = get_area_turfs(where_to_move) // Grabs the turfs from said area
target_turf = pick(target_area_turfs) // Grabs a single turf from the entire list
movement_goal = pick(target_area_turfs) // Grabs a single turf from the entire list
/obj/singularity/energy_ball/Move(target, direction)
// Energy balls move through everything, make it a forceMove.
if(miniball)
return ..()
if(!loc || !target)
// Don't move while nowhere.
return FALSE
if((locate(/obj/machinery/shield) in target) || (locate(/obj/machinery/field/containment) in target))
// We can't go that way, and we're now in range of a containment field. Stop long-distance movement.
movement_goal = null
move_target = null
GLOB.move_manager.stop_looping(src)
return FALSE
// Energy balls move through everything, make it a forceMove.
forceMove(target, direction)
return TRUE
/obj/singularity/energy_ball/forceMove(target)
. = ..()
if(miniball || !isturf(target))
return
for(var/mob/M in target)
dust_mobs(M)
// Energy balls do not drift in space.
/obj/singularity/energy_ball/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
return TRUE
/obj/singularity/energy_ball/proc/handle_energy()
if(energy >= energy_to_raise)
energy_to_lower = energy_to_raise - 20
@@ -248,9 +274,6 @@
EB.parent_energy_ball = src
EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36), orbit_layer = EB.layer)
/obj/singularity/energy_ball/Bump(atom/A)
dust_mobs(A)
/obj/singularity/energy_ball/Bumped(atom/movable/AM)
dust_mobs(AM)
@@ -294,17 +317,15 @@
parent_energy_ball = null
/obj/singularity/energy_ball/proc/dust_mobs(atom/A)
if(isliving(A))
var/mob/living/L = A
if(L.incorporeal_move || L.status_flags & GODMODE)
return
if(!iscarbon(A))
if(!isliving(A))
return
var/mob/living/L = A
if(L.incorporeal_move || L.status_flags & GODMODE)
return
for(var/obj/machinery/power/grounding_rod/GR in orange(src, 2))
if(GR.anchored)
return
var/mob/living/carbon/C = A
C.dust()
L.dust()
/proc/tesla_zap(atom/source, zap_range = 3, power, zap_flags = ZAP_DEFAULT_FLAGS, list/shocked_targets = list())
if(QDELETED(source))