mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-15 04:01:41 +00:00
Adds spell cards. They're a wizard spell that shoots a burst of 5 semi-accurate homing cards. Projectiles now have a homing framework, complete with some variant of simulated inaccuracy. The said wizard spell will make use of a new mob component, that allows that mob to select targets by moving their cursor near them. It will give a visual and lock onto the nearest mob to the cursor, allowing the homing projectiles to target on the locked on mob/object. Removes colliding variable from projectiles - We never used it after Bump was refactored to Collide. Images soon when I get the lockon datum-components to work.
226 lines
6.7 KiB
Plaintext
226 lines
6.7 KiB
Plaintext
//Designed for things that need precision trajectories like projectiles.
|
|
//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels.
|
|
|
|
//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf.
|
|
|
|
#define RETURN_PRECISE_POSITION(A) new /datum/position(A)
|
|
#define RETURN_PRECISE_POINT(A) new /datum/point(A)
|
|
|
|
#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)}
|
|
#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)}
|
|
|
|
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
|
|
var/datum/point/P = new
|
|
P.x = a.x + (b.x - a.x) / 2
|
|
P.y = a.y + (b.y - a.y) / 2
|
|
P.z = a.z
|
|
return P
|
|
|
|
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
|
|
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
|
|
|
|
/proc/angle_between_points(datum/point/a, datum/point/b)
|
|
return ATAN2((b.y - a.y), (b.x - a.x))
|
|
|
|
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
|
|
var/x = 0
|
|
var/y = 0
|
|
var/z = 0
|
|
var/pixel_x = 0
|
|
var/pixel_y = 0
|
|
|
|
/datum/position/proc/valid()
|
|
return x && y && z && !isnull(pixel_x) && !isnull(pixel_y)
|
|
|
|
/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point.
|
|
if(istype(_x, /datum/point))
|
|
var/datum/point/P = _x
|
|
var/turf/T = P.return_turf()
|
|
_x = T.x
|
|
_y = T.y
|
|
_z = T.z
|
|
_pixel_x = P.return_px()
|
|
_pixel_y = P.return_py()
|
|
else if(isatom(_x))
|
|
var/atom/A = _x
|
|
_x = A.x
|
|
_y = A.y
|
|
_z = A.z
|
|
_pixel_x = A.pixel_x
|
|
_pixel_y = A.pixel_y
|
|
x = _x
|
|
y = _y
|
|
z = _z
|
|
pixel_x = _pixel_x
|
|
pixel_y = _pixel_y
|
|
|
|
/datum/position/proc/return_turf()
|
|
return locate(x, y, z)
|
|
|
|
/datum/position/proc/return_px()
|
|
return pixel_x
|
|
|
|
/datum/position/proc/return_py()
|
|
return pixel_y
|
|
|
|
/datum/position/proc/return_point()
|
|
return new /datum/point(src)
|
|
|
|
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
|
|
var/x = 0
|
|
var/y = 0
|
|
var/z = 0
|
|
|
|
/datum/point/proc/valid()
|
|
return x && y && z
|
|
|
|
/datum/point/proc/copy_to(datum/point/p = new)
|
|
p.x = x
|
|
p.y = y
|
|
p.z = z
|
|
return p
|
|
|
|
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
|
|
if(istype(_x, /datum/position))
|
|
var/datum/position/P = _x
|
|
_x = P.x
|
|
_y = P.y
|
|
_z = P.z
|
|
_pixel_x = P.pixel_x
|
|
_pixel_y = P.pixel_y
|
|
else if(istype(_x, /atom))
|
|
var/atom/A = _x
|
|
_x = A.x
|
|
_y = A.y
|
|
_z = A.z
|
|
_pixel_x = A.pixel_x
|
|
_pixel_y = A.pixel_y
|
|
initialize_location(_x, _y, _z, _pixel_x, _pixel_y)
|
|
|
|
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
|
|
if(!isnull(tile_x))
|
|
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + 1
|
|
if(!isnull(tile_y))
|
|
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2 + p_y + 1
|
|
if(!isnull(tile_z))
|
|
z = tile_z
|
|
|
|
/datum/point/proc/debug_out()
|
|
var/turf/T = return_turf()
|
|
return "\ref[src] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
|
|
|
|
/datum/point/proc/move_atom_to_src(atom/movable/AM)
|
|
AM.forceMove(return_turf())
|
|
AM.pixel_x = return_px()
|
|
AM.pixel_y = return_py()
|
|
|
|
/datum/point/proc/return_turf()
|
|
return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
|
|
|
|
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
|
|
return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
|
|
|
|
/datum/point/proc/return_position()
|
|
return new /datum/position(src)
|
|
|
|
/datum/point/proc/return_px()
|
|
return MODULUS(x, world.icon_size) - 16 - 1
|
|
|
|
/datum/point/proc/return_py()
|
|
return MODULUS(y, world.icon_size) - 16 - 1
|
|
|
|
/datum/point/vector
|
|
var/speed = 32 //pixels per iteration
|
|
var/iteration = 0
|
|
var/angle = 0
|
|
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
|
|
var/mpy = 0
|
|
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
|
|
var/starting_y = 0
|
|
var/starting_z = 0
|
|
|
|
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed, initial_increment = 0)
|
|
..()
|
|
initialize_trajectory(_speed, _angle)
|
|
if(initial_increment)
|
|
increment(initial_increment)
|
|
|
|
/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
|
|
. = ..()
|
|
starting_x = x
|
|
starting_y = y
|
|
starting_z = z
|
|
|
|
/datum/point/vector/copy_to(datum/point/vector/v = new)
|
|
..(v)
|
|
v.speed = speed
|
|
v.iteration = iteration
|
|
v.angle = angle
|
|
v.mpx = mpx
|
|
v.mpy = mpy
|
|
v.starting_x = starting_x
|
|
v.starting_y = starting_y
|
|
v.starting_z = starting_z
|
|
return v
|
|
|
|
/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle)
|
|
if(!isnull(pixel_speed))
|
|
speed = pixel_speed
|
|
set_angle(new_angle)
|
|
|
|
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
|
|
if(isnull(angle))
|
|
return
|
|
angle = new_angle
|
|
update_offsets()
|
|
|
|
/datum/point/vector/proc/update_offsets()
|
|
mpx = sin(angle) * speed
|
|
mpy = cos(angle) * speed
|
|
|
|
/datum/point/vector/proc/set_speed(new_speed)
|
|
if(isnull(new_speed) || speed == new_speed)
|
|
return
|
|
speed = new_speed
|
|
update_offsets()
|
|
|
|
/datum/point/vector/proc/increment(multiplier = 1)
|
|
iteration++
|
|
x += mpx * 1
|
|
y += mpy * 1
|
|
|
|
/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE)
|
|
var/datum/point/vector/v = copy_to()
|
|
if(force_simulate)
|
|
for(var/i in 1 to amount)
|
|
v.increment(multiplier)
|
|
else
|
|
v.increment(multiplier * amount)
|
|
return v
|
|
|
|
/datum/point/vector/proc/on_z_change()
|
|
return
|
|
|
|
/datum/point/vector/processed //pixel_speed is per decisecond.
|
|
var/last_process = 0
|
|
var/last_move = 0
|
|
var/paused = FALSE
|
|
|
|
/datum/point/vector/processed/Destroy()
|
|
STOP_PROCESSING(SSprojectiles, src)
|
|
|
|
/datum/point/vector/processed/proc/start()
|
|
last_process = world.time
|
|
last_move = world.time
|
|
START_PROCESSING(SSprojectiles, src)
|
|
|
|
/datum/point/vector/processed/process()
|
|
if(paused)
|
|
last_move += world.time - last_process
|
|
last_process = world.time
|
|
return
|
|
var/needed_time = world.time - last_move
|
|
last_process = world.time
|
|
last_move = world.time
|
|
increment(needed_time / SSprojectiles.wait)
|