Files
Bubberstation/code/__HELPERS/maths.dm
SkyratBot f3d405da28 [MIRROR] Modular Shield Generators, Also Adds a new perimeter algorithm to _helpers/math_dm [MDB IGNORE] (#21426)
* Modular Shield Generators, Also Adds a new perimeter algorithm to _helpers/math_dm (#75328)

Disclaimer im mostly making this pr for the experience if anything.
Also anyone can replace the sprites if they want to I`m just making them
so itl be done, idk what a plasteel machine is supposed to look like.

The circuit boards to make these machines are unlocked by the advanced
power manipulation tech

The entire shield

- Has one big healthpool (base of 50)
- Has a configurable radius
- Takes damage from hot gas and emp
- Has regeneration (it gets nerfed by alot of things that I have to talk
about separately)
- When the shield is broken the generator must regenerate at 0.25x speed
before coming online again
- can be configured to either only generate a shield externally (in
space) or both internally and externally at the cost of half the regen
rate
- Regen rate is also reduced based on the ratio of the shield radius and
the max radius that the generator is capable of generating
- Alot more that im too tired to explain at the moment

The base stats of the shield (max health, regen rate, max radius) all
scale with the tier of parts used in the main generator.

4 other machines (3 are finished code-wise as of now) the charger (regen
rate), the relay (max radius) the well (max health) the node (allows for
more connections) can all be connected to the main generator to boost
its core stats based on their part tiers

In the future there is a possibility of specialization / modifications
to the forcefield at the cost to the core stats

* Modular Shield Generators, Also Adds a new perimeter algorithm to _helpers/math_dm

---------

Co-authored-by: moocowswag <62126254+moocowswag@users.noreply.github.com>
2023-05-25 23:13:30 +01:00

193 lines
6.9 KiB
Plaintext

///Calculate the angle between two movables and the west|east coordinate
/proc/get_angle(atom/movable/start, atom/movable/end)//For beams.
if(!start || !end)
return 0
var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y)
var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x)
if(!dy)
return (dx >= 0) ? 90 : 270
. = arctan(dx/dy)
if(dy < 0)
. += 180
else if(dx < 0)
. += 360
/// Angle between two arbitrary points and horizontal line same as [/proc/get_angle]
/proc/get_angle_raw(start_x, start_y, start_pixel_x, start_pixel_y, end_x, end_y, end_pixel_x, end_pixel_y)
var/dy = (32 * end_y + end_pixel_y) - (32 * start_y + start_pixel_y)
var/dx = (32 * end_x + end_pixel_x) - (32 * start_x + start_pixel_x)
if(!dy)
return (dx >= 0) ? 90 : 270
. = arctan(dx/dy)
if(dy < 0)
. += 180
else if(dx < 0)
. += 360
///for getting the angle when animating something's pixel_x and pixel_y
/proc/get_pixel_angle(y, x)
if(!y)
return (x >= 0) ? 90 : 270
. = arctan(x/y)
if(y < 0)
. += 180
else if(x < 0)
. += 360
/**
* Get a list of turfs in a line from `starting_atom` to `ending_atom`.
*
* Uses the ultra-fast [Bresenham Line-Drawing Algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm).
*/
/proc/get_line(atom/starting_atom, atom/ending_atom)
var/current_x_step = starting_atom.x//start at x and y, then add 1 or -1 to these to get every turf from starting_atom to ending_atom
var/current_y_step = starting_atom.y
var/starting_z = starting_atom.z
var/list/line = list(get_turf(starting_atom))//get_turf(atom) is faster than locate(x, y, z)
var/x_distance = ending_atom.x - current_x_step //x distance
var/y_distance = ending_atom.y - current_y_step
var/abs_x_distance = abs(x_distance)//Absolute value of x distance
var/abs_y_distance = abs(y_distance)
var/x_distance_sign = SIGN(x_distance) //Sign of x distance (+ or -)
var/y_distance_sign = SIGN(y_distance)
var/x = abs_x_distance >> 1 //Counters for steps taken, setting to distance/2
var/y = abs_y_distance >> 1 //Bit-shifting makes me l33t. It also makes get_line() unnessecarrily fast.
if(abs_x_distance >= abs_y_distance) //x distance is greater than y
for(var/distance_counter in 0 to (abs_x_distance - 1))//It'll take abs_x_distance steps to get there
y += abs_y_distance
if(y >= abs_x_distance) //Every abs_y_distance steps, step once in y direction
y -= abs_x_distance
current_y_step += y_distance_sign
current_x_step += x_distance_sign //Step on in x direction
line += locate(current_x_step, current_y_step, starting_z)//Add the turf to the list
else
for(var/distance_counter in 0 to (abs_y_distance - 1))
x += abs_x_distance
if(x >= abs_y_distance)
x -= abs_y_distance
current_x_step += x_distance_sign
current_y_step += y_distance_sign
line += locate(current_x_step, current_y_step, starting_z)
return line
/**
* Get a list of turfs in a perimeter given the `center_atom` and `radius`.
* Automatically rounds down decimals and does not accept values less than positive 1 as they dont play well with it.
* Is efficient on large circles but ugly on small ones
* Uses [Jesko`s method to the midpoint circle Algorithm](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm).
*/
/proc/get_perimeter(atom/center, radius)
if(radius < 1)
return
var/rounded_radius = round(radius)
var/x = center.x
var/y = center.y
var/z = center.z
var/t1 = rounded_radius/16
var/dx = rounded_radius
var/dy = 0
var/t2
var/list/perimeter = list()
while(dx >= dy)
perimeter += locate(x + dx, y + dy, z)
perimeter += locate(x - dx, y + dy, z)
perimeter += locate(x + dx, y - dy, z)
perimeter += locate(x - dx, y - dy, z)
perimeter += locate(x + dy, y + dx, z)
perimeter += locate(x - dy, y + dx, z)
perimeter += locate(x + dy, y - dx, z)
perimeter += locate(x - dy, y - dx, z)
dy += 1
t1 += dy
t2 = t1 - dx
if(t2 > 0)
t1 = t2
dx -= 1
return perimeter
///Format a power value in W, kW, MW, or GW.
/proc/display_power(powerused)
if(powerused < 1000) //Less than a kW
return "[powerused] W"
else if(powerused < 1000000) //Less than a MW
return "[round((powerused * 0.001),0.01)] kW"
else if(powerused < 1000000000) //Less than a GW
return "[round((powerused * 0.000001),0.001)] MW"
return "[round((powerused * 0.000000001),0.0001)] GW"
///Format an energy value in J, kJ, MJ, or GJ. 1W = 1J/s.
/proc/display_joules(units)
if (units < 1000) // Less than a kJ
return "[round(units, 0.1)] J"
else if (units < 1000000) // Less than a MJ
return "[round(units * 0.001, 0.01)] kJ"
else if (units < 1000000000) // Less than a GJ
return "[round(units * 0.000001, 0.001)] MJ"
return "[round(units * 0.000000001, 0.0001)] GJ"
/proc/joules_to_energy(joules)
return joules * (1 SECONDS) / SSmachines.wait
/proc/energy_to_joules(energy_units)
return energy_units * SSmachines.wait / (1 SECONDS)
///Format an energy value measured in Power Cell units.
/proc/display_energy(units)
// APCs process every (SSmachines.wait * 0.1) seconds, and turn 1 W of
// excess power into watts when charging cells.
// With the current configuration of wait=20 and CELLRATE=0.002, this
// means that one unit is 1 kJ.
return display_joules(energy_to_joules(units) WATTS)
///chances are 1:value. anyprob(1) will always return true
/proc/anyprob(value)
return (rand(1,value) == value)
///counts the number of bits in Byond's 16-bit width field, in constant time and memory!
/proc/bit_count(bit_field)
var/temp = bit_field - ((bit_field >> 1) & 46811) - ((bit_field >> 2) & 37449) //0133333 and 0111111 respectively
temp = ((temp + (temp >> 3)) & 29127) % 63 //070707
return temp
/// Returns the name of the mathematical tuple of same length as the number arg (rounded down).
/proc/make_tuple(number)
var/static/list/units_prefix = list("", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem")
var/static/list/tens_prefix = list("", "decem", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nongen")
var/static/list/one_to_nine = list("monuple", "double", "triple", "quadruple", "quintuple", "sextuple", "septuple", "octuple", "nonuple")
number = round(number)
switch(number)
if(0)
return "empty tuple"
if(1 to 9)
return one_to_nine[number]
if(10 to 19)
return "[units_prefix[(number%10)+1]]decuple"
if(20 to 99)
return "[units_prefix[(number%10)+1]][tens_prefix[round((number % 100)/10)+1]]tuple"
if(100)
return "centuple"
else //It gets too tedious to use latin prefixes from here.
return "[number]-tuple"
/// Takes a value, and a threshold it has to at least match
/// returns the correctly signed value max'd to the threshold
/proc/at_least(new_value, threshold)
var/sign = SIGN(new_value)
// SIGN will return 0 if the value is 0, so we just go to the positive threshold
if(!sign)
return threshold
if(sign == 1)
return max(new_value, threshold)
if(sign == -1)
return min(new_value, threshold * -1)