mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-07-22 13:02:49 +01:00
308 lines
7.9 KiB
Plaintext
308 lines
7.9 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)
|
|
|
|
/**
|
|
* Stores x/y/z and pixel_x/pixel_y
|
|
*/
|
|
/datum/position
|
|
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(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
|
|
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)
|
|
|
|
// todo: shouldn't be global scope
|
|
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
|
|
var/datum/point/created = new
|
|
created.x = a.x + (b.x - a.x) / 2
|
|
created.y = a.y + (b.y - a.y) / 2
|
|
created.z = a.z
|
|
return created
|
|
|
|
// todo: shouldn't be global scope
|
|
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
|
|
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
|
|
|
|
// todo: shouldn't be global scope
|
|
/**
|
|
* @return angle between A and B, as degrees **clockwise from north**
|
|
*/
|
|
/proc/angle_between_points(datum/point/a, datum/point/b)
|
|
return arctan((b.y - a.y), (b.x - a.x))
|
|
|
|
/**
|
|
* A precise point on the map.
|
|
*
|
|
* x/y are absolute pixels from map edge, so 1, 1 = the lower-left most pixel on the zlevel, not the center of turf (1,1)!
|
|
*/
|
|
/datum/point
|
|
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
|
|
|
|
// todo: get first of first argument wrapping, use to_point() on /datum/position and from_atom() / from_position() on /datum/point
|
|
/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]"
|
|
|
|
/**
|
|
* angle is clockwise from north
|
|
*
|
|
* @return self
|
|
*/
|
|
/datum/point/proc/shift_in_projectile_angle(angle, distance)
|
|
x += sin(angle) * distance
|
|
y += cos(angle) * distance
|
|
return src
|
|
|
|
/**
|
|
* doesn't use set base pixel x/y
|
|
*
|
|
* if not on a turf, we return null
|
|
*/
|
|
/datum/point/proc/instantiate_movable_with_unmanaged_offsets(typepath, ...)
|
|
ASSERT(ispath(typepath, /atom/movable))
|
|
// todo: inline everything
|
|
var/turf/where = return_turf()
|
|
if(!where)
|
|
return
|
|
var/atom/movable/created = new typepath(arglist(list(where) + args.Copy(2)))
|
|
created.pixel_x = return_px()
|
|
created.pixel_y = return_py()
|
|
return created
|
|
|
|
/**
|
|
* return rounded pixel x
|
|
*/
|
|
/datum/point/proc/return_px()
|
|
// 1 = -15,
|
|
// 32 = +16
|
|
// we start at 16, 16
|
|
. = x % WORLD_ICON_SIZE
|
|
if(!.)
|
|
return WORLD_ICON_SIZE * 0.5
|
|
. -= WORLD_ICON_SIZE * 0.5
|
|
|
|
/**
|
|
* return rounded pixel y
|
|
*/
|
|
/datum/point/proc/return_py()
|
|
// 1 = -15,
|
|
// 32 = +16
|
|
// we start at 16, 16
|
|
. = y % WORLD_ICON_SIZE
|
|
if(!.)
|
|
return WORLD_ICON_SIZE * 0.5
|
|
. -= WORLD_ICON_SIZE * 0.5
|
|
|
|
/**
|
|
* return turf
|
|
*/
|
|
/datum/point/proc/return_turf()
|
|
return locate(
|
|
ceil(floor(x) / WORLD_ICON_SIZE),
|
|
ceil(floor(y) / WORLD_ICON_SIZE),
|
|
z,
|
|
)
|
|
|
|
/**
|
|
* extract closest in-bounds turf
|
|
*
|
|
* does not check for map transitions
|
|
*/
|
|
/datum/point/proc/clamped_return_turf()
|
|
return locate(
|
|
clamp(ceil(floor(x) / WORLD_ICON_SIZE), 1, world.maxx),
|
|
clamp(ceil(floor(y) / WORLD_ICON_SIZE), 1, world.maxy),
|
|
z,
|
|
)
|
|
|
|
/**
|
|
* return list(x, y, z)
|
|
*/
|
|
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
|
|
return list(
|
|
ceil(floor(x) / WORLD_ICON_SIZE),
|
|
ceil(floor(y) / WORLD_ICON_SIZE),
|
|
z,
|
|
)
|
|
|
|
/datum/point/vector
|
|
/// Pixels per iteration
|
|
var/speed = 32
|
|
var/iteration = 0
|
|
var/angle = 0
|
|
/// Calculated x movement amounts to prevent having to do trig every step.
|
|
var/mpx = 0
|
|
/// Calculated 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)
|
|
|
|
/// Calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
|
|
/datum/point/vector/proc/set_angle(new_angle)
|
|
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 * (multiplier)
|
|
y += mpy * (multiplier)
|
|
|
|
/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)
|
|
return ..()
|
|
|
|
/datum/point/vector/processed/proc/start()
|
|
last_process = world.time
|
|
last_move = world.time
|
|
START_PROCESSING(SSprojectiles, src)
|
|
|
|
/datum/point/vector/processed/process(delta_time)
|
|
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)
|