From c1a47cf20bddfa247ea8dc23b3047f2cb1a420d6 Mon Sep 17 00:00:00 2001 From: Fluffy <65877598+FluffyGhoster@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:07:58 +0200 Subject: [PATCH] Matrix (in)sanity (#19366) Ported the various matrix procs and defines from TG. Updated our snowflake procs with the Bay's current version. Refactor of our orbit proc to align with the updated procs, tesla energy ball too. --- aurorastation.dme | 1 + code/__DEFINES/matrices.dm | 27 ++ code/__HELPERS/matrices.dm | 330 +++++++++++++++--- code/modules/orbit/orbit.dm | 13 +- code/modules/power/tesla/energy_ball.dm | 2 +- .../fluffyghost-sanermatrixprocs.yml | 60 ++++ 6 files changed, 366 insertions(+), 67 deletions(-) create mode 100644 code/__DEFINES/matrices.dm create mode 100644 html/changelogs/fluffyghost-sanermatrixprocs.yml diff --git a/aurorastation.dme b/aurorastation.dme index 9dbccccce3e..b21741bf95c 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -81,6 +81,7 @@ #include "code\__DEFINES\materials.dm" #include "code\__DEFINES\math_physics.dm" #include "code\__DEFINES\maths.dm" +#include "code\__DEFINES\matrices.dm" #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\minimap.dm" #include "code\__DEFINES\mining.dm" diff --git a/code/__DEFINES/matrices.dm b/code/__DEFINES/matrices.dm new file mode 100644 index 00000000000..26ff5a7232a --- /dev/null +++ b/code/__DEFINES/matrices.dm @@ -0,0 +1,27 @@ +/// Helper macro for creating a matrix at the given offsets. +/// Works at compile time. +#define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y)) +/// The color matrix of an image which colors haven't been altered. Does nothing. +#define COLOR_MATRIX_IDENTITY list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0) +/// Color inversion +#define COLOR_MATRIX_INVERT list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0) +///Sepiatone +#define COLOR_MATRIX_SEPIATONE list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0) +///Grayscale +#define COLOR_MATRIX_GRAYSCALE list(0.33,0.33,0.33,0, 0.59,0.59,0.59,0, 0.11,0.11,0.11,0, 0,0,0,1, 0,0,0,0) +///Polaroid colors +#define COLOR_MATRIX_POLAROID list(1.438,-0.062,-0.062,0, -0.122,1.378,-0.122,0, -0.016,-0.016,1.483,0, 0,0,0,1, 0,0,0,0) +/// Converts reds to blue, green to red and blue to green. +#define COLOR_MATRIX_BRG list(0,0,1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1, 0,0,0,0) +/// Black & White +#define COLOR_MATRIX_BLACK_WHITE list(1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 0,0,0,1, -1,-1,-1,0) +/** + * Adds/subtracts overall lightness + * 0 is identity, 1 makes everything white, -1 makes everything black + */ +#define COLOR_MATRIX_LIGHTNESS(power) list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0) +/** + * Changes distance colors have from rgb(127,127,127) grey + * 1 is identity. 0 makes everything grey >1 blows out colors and greys + */ +#define COLOR_MATRIX_CONTRAST(val) list(val,0,0,0, 0,val,0,0, 0,0,val,0, 0,0,0,1, (1-val)*0.5,(1-val)*0.5,(1-val)*0.5,0) diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 03b2f5fc43b..661a7aa06a6 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -1,56 +1,275 @@ +//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. +#define LUMA_R 0.213 +#define LUMA_G 0.715 +#define LUMA_B 0.072 + +/// Datum which stores information about a matrix decomposed with decompose(). +/datum/decompose_matrix + ///? + var/scale_x = 1 + ///? + var/scale_y = 1 + ///? + var/rotation = 0 + ///? + var/shift_x = 0 + ///? + var/shift_y = 0 + +/// Decomposes a matrix into scale, shift and rotation. +/// +/// If other operations were applied on the matrix, such as shearing, the result +/// will not be precise. +/// +/// Negative scales are now supported. =) +/matrix/proc/decompose() + var/datum/decompose_matrix/decompose_matrix = new + . = decompose_matrix + var/flip_sign = (a*e - b*d < 0)? -1 : 1 // Det < 0 => only 1 axis is flipped - start doing some sign flipping + // If both axis are flipped, nothing bad happens and Det >= 0, it just treats it like a 180° rotation + // If only 1 axis is flipped, we need to flip one direction - in this case X, so we flip a, b and the x scaling + decompose_matrix.scale_x = sqrt(a * a + d * d) * flip_sign + decompose_matrix.scale_y = sqrt(b * b + e * e) + decompose_matrix.shift_x = c + decompose_matrix.shift_y = f + if(!decompose_matrix.scale_x || !decompose_matrix.scale_y) + return + // If only translated, scaled and rotated, a/xs == e/ys and -d/xs == b/xy + var/cossine = (a/decompose_matrix.scale_x + e/decompose_matrix.scale_y) / 2 + var/sine = (b/decompose_matrix.scale_y - d/decompose_matrix.scale_x) / 2 * flip_sign + decompose_matrix.rotation = arctan(cossine, sine) * flip_sign + /matrix/proc/TurnTo(old_angle, new_angle) . = new_angle - old_angle Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT +/** + * Shear the transform on either or both axes. + * * x - X axis shearing + * * y - Y axis shearing + */ +/matrix/proc/Shear(x, y) + return Multiply(matrix(1, x, 0, y, 1, 0)) -/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3) - if(!segments) - return - var/segment = 360/segments - if(!clockwise) - segment = -segment - var/list/matrices = list() - for(var/i in 1 to segments-1) - var/matrix/M = matrix(transform) - M.Turn(segment*i) - matrices += M - var/matrix/last = matrix(transform) - matrices += last +//Dumps the matrix data in format a-f +/matrix/proc/tolist() + . = list() + . += a + . += b + . += c + . += d + . += e + . += f - speed /= segments +//Dumps the matrix data in a matrix-grid format +/* +a d 0 +b e 0 +c f 1 +*/ +/matrix/proc/togrid() + . = list() + . += a + . += d + . += 0 + . += b + . += e + . += 0 + . += c + . += f + . += 1 - animate(src, transform = matrices[1], time = speed, loops) - for(var/i in 2 to segments) //2 because 1 is covered above - animate(transform = matrices[i], time = speed) - //doesn't have an object argument because this is "Stacking" with the animate call above - //3 billion% intentional +///The X pixel offset of this matrix +/matrix/proc/get_x_shift() + . = c -/atom/proc/shake_animation(var/intensity = 8) +///The Y pixel offset of this matrix +/matrix/proc/get_y_shift() + . = f + +///////////////////// +// COLOUR MATRICES // +///////////////////// + +/* Documenting a couple of potentially useful color matrices here to inspire ideas +// Greyscale - indentical to saturation @ 0 +list(LUMA_R,LUMA_R,LUMA_R,0, LUMA_G,LUMA_G,LUMA_G,0, LUMA_B,LUMA_B,LUMA_B,0, 0,0,0,1, 0,0,0,0) + +// Color inversion +list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0) + +// Sepiatone +list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0) +*/ + +//Changes distance hues have from grey while maintaining the overall lightness. Greys are unaffected. +//1 is identity, 0 is greyscale, >1 oversaturates colors +/proc/color_matrix_saturation(value) + var/inv = 1 - value + var/R = round(LUMA_R * inv, 0.001) + var/G = round(LUMA_G * inv, 0.001) + var/B = round(LUMA_B * inv, 0.001) + + return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0) + +//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys +//0 is identity, 120 moves reds to greens, 240 moves reds to blues +/proc/color_matrix_rotate_hue(angle) + var/sin = sin(angle) + var/cos = cos(angle) + var/cos_inv_third = 0.333*(1-cos) + var/sqrt3_sin = sqrt(3)*sin + return list( +round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), 0, +round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), 0, +round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), 0, +0,0,0,1, +0,0,0,0) + +//These next three rotate values about one axis only +//x is the red axis, y is the green axis, z is the blue axis. +/proc/color_matrix_rotate_x(angle) + var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001) + return list(1,0,0,0, 0,cosval,sinval,0, 0,-sinval,cosval,0, 0,0,0,1, 0,0,0,0) + +/proc/color_matrix_rotate_y(angle) + var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001) + return list(cosval,0,-sinval,0, 0,1,0,0, sinval,0,cosval,0, 0,0,0,1, 0,0,0,0) + +/proc/color_matrix_rotate_z(angle) + var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001) + return list(cosval,sinval,0,0, -sinval,cosval,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0) + + +//Returns a matrix addition of A with B +/proc/color_matrix_add(list/A, list/B) + if(!istype(A) || !istype(B)) + return COLOR_MATRIX_IDENTITY + if(A.len != 20 || B.len != 20) + return COLOR_MATRIX_IDENTITY + var/list/output = list() + output.len = 20 + for(var/value in 1 to 20) + output[value] = A[value] + B[value] + return output + +//Returns a matrix multiplication of A with B +/proc/color_matrix_multiply(list/A, list/B) + if(!istype(A) || !istype(B)) + return COLOR_MATRIX_IDENTITY + if(A.len != 20 || B.len != 20) + return COLOR_MATRIX_IDENTITY + var/list/output = list() + output.len = 20 + var/x = 1 + var/y = 1 + var/offset = 0 + for(y in 1 to 5) + offset = (y-1)*4 + for(x in 1 to 4) + output[offset+x] = round(A[offset+1]*B[x] + A[offset+2]*B[x+4] + A[offset+3]*B[x+8] + A[offset+4]*B[x+12]+(y == 5?B[x+16]:0), 0.001) + return output + +/** + * Converts RGB shorthands into RGBA matrices complete of constants rows (ergo a 20 keys list in byond). + * if return_identity_on_fail is true, stack_trace is called instead of CRASH, and an identity is returned. + */ +/proc/color_to_full_rgba_matrix(color, return_identity_on_fail = TRUE) + if(!color) + return COLOR_MATRIX_IDENTITY + if(istext(color)) + var/list/L = rgb2num(color) + if(!L) + var/message = "Invalid/unsupported color ([color]) argument in color_to_full_rgba_matrix()" + if(return_identity_on_fail) + stack_trace(message) + return COLOR_MATRIX_IDENTITY + CRASH(message) + return list(L[1]/255,0,0,0, 0,L[2]/255,0,0, 0,0,L[3]/255,0, 0,0,0,L.len>3?L[4]/255:1, 0,0,0,0) + if(!islist(color)) //invalid format + CRASH("Invalid/unsupported color ([color]) argument in color_to_full_rgba_matrix()") + var/list/L = color + switch(L.len) + if(3 to 5) // row-by-row hexadecimals + . = list() + for(var/a in 1 to L.len) + var/list/rgb = rgb2num(L[a]) + for(var/b in rgb) + . += b/255 + if(length(rgb) % 4) // RGB has no alpha instruction + . += a != 4 ? 0 : 1 + if(L.len < 4) //missing both alphas and constants rows + . += list(0,0,0,1, 0,0,0,0) + else if(L.len < 5) //missing constants row + . += list(0,0,0,0) + if(9 to 12) //RGB + . = list(L[1],L[2],L[3],0, L[4],L[5],L[6],0, L[7],L[8],L[9],0, 0,0,0,1) + for(var/b in 1 to 3) //missing constants row + . += L.len < 9+b ? 0 : L[9+b] + . += 0 + if(16 to 20) // RGBA + . = L.Copy() + if(L.len < 20) //missing constants row + for(var/b in 1 to 20-L.len) + . += 0 + else + var/message = "Invalid/unsupported color (list of length [L.len]) argument in color_to_full_rgba_matrix()" + if(return_identity_on_fail) + stack_trace(message) + return COLOR_MATRIX_IDENTITY + CRASH(message) + + +/*############################ + AURORA SNOWFLAKE SECTION + (Most are from Bay) +############################*/ + +/** + * Performs a spin/rotation animation on the atom's sprite. + * + * **Parameters**: + * - `speed` (int) - How quickly the atom should rotate. + * - `loops` (int) - How many times the spin animation should occur. Set to `-1` for infinite looping. + */ +/atom/proc/SpinAnimation(speed = 10, loops = -1) + var/matrix/m120 = matrix(transform).Update(rotation = 120) + var/matrix/m240 = matrix(transform).Update(rotation = 240) + var/matrix/m360 = matrix(transform).Update(rotation = 360) + animate(src, transform = m120, time = speed / 3, loops) + animate(transform = m240, time = speed / 3) + animate(transform = m360, time = speed / 3) + +/** + * Performs a shaking animation on the atom's sprite. + * + * **Parameters**: + * - `intensity` integer - The intensity of the shaking. + */ +/atom/proc/shake_animation(intensity = 8) var/init_px = pixel_x var/shake_dir = pick(-1, 1) - animate(src, transform=turn(matrix(), intensity*shake_dir), pixel_x=init_px + 2*shake_dir, time=1) + animate( + src, + transform = matrix().Update(rotation = intensity * shake_dir), + pixel_x = init_px + 2 * shake_dir, + time = 1 + ) animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING) - return intensity -// Color matrices: -//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. -#define LUMR 0.2126 -#define LUMG 0.7152 -#define LUMB 0.0722 - -//Still need color matrix addition, negation, and multiplication. - -//Returns an identity color matrix which does nothing +///Returns an identity color matrix which does nothing /proc/color_identity() + RETURN_TYPE(/list) return list(1,0,0, 0,1,0, 0,0,1) -//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites //TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) +///Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites /proc/color_rotation(angle) + RETURN_TYPE(/list) if(angle == 0) return color_identity() - angle = Clamp(angle, -180, 180) + angle = clamp(angle, -180, 180) var/cos = cos(angle) var/sin = sin(angle) @@ -58,19 +277,12 @@ var/constB = 0.140 var/constC = -0.283 return list( - LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR), - LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG, - LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB + LUMA_R + cos * (1-LUMA_R) + sin * -LUMA_R, LUMA_R + cos * -LUMA_R + sin * constA, LUMA_R + cos * -LUMA_R + sin * -(1-LUMA_R), + LUMA_G + cos * -LUMA_G + sin * -LUMA_G, LUMA_G + cos * (1-LUMA_G) + sin * constB, LUMA_G + cos * -LUMA_G + sin * LUMA_G, + LUMA_B + cos * -LUMA_B + sin * (1-LUMA_B), LUMA_B + cos * -LUMA_B + sin * constC, LUMA_B + cos * (1-LUMA_B) + sin * LUMA_B ) -//Makes everything brighter or darker without regard to existing color or brightness -/proc/color_brightness(power) - power = Clamp(power, -255, 255) - power = power/255 - - return list(1,0,0, 0,1,0, 0,0,1, power,power,power) - -/var/list/delta_index = list( +var/global/list/delta_index = list( 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, @@ -83,9 +295,10 @@ 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, 10.0) -//Exxagerates or removes brightness +///Exxagerates or removes brightness /proc/color_contrast(value) - value = Clamp(value, -100, 100) + RETURN_TYPE(/list) + value = round(clamp(value, -100, 100)) if(value == 0) return color_identity() @@ -104,24 +317,22 @@ var/add = 0.5 * (127-x) / 255 return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add) -//Exxagerates or removes colors +///Exxagerates or removes colors /proc/color_saturation(value as num) + RETURN_TYPE(/list) if(value == 0) return color_identity() - value = Clamp(value, -100, 100) + value = clamp(value, -100, 100) if(value > 0) value *= 3 var/x = 1 + value / 100 var/inv = 1 - x - var/R = LUMR * inv - var/G = LUMG * inv - var/B = LUMB * inv + var/R = LUMA_R * inv + var/G = LUMA_G * inv + var/B = LUMA_B * inv return list(R + x,R,R, G,G + x,G, B,B,B + x) -#undef LUMR -#undef LUMG -#undef LUMB /** * @@ -131,10 +342,10 @@ * Do make sure your lists actually have this many elements */ /proc/multiply_matrices(list/A, list/B, m, n, p) - var/list/result = list() - result.len = m * p + RETURN_TYPE(/list) + var/list/result = new (m * p) - if(A.len == m*n && B.len == n*p) + if(length(A) == m*n && length(B) == n*p) for(var/row = 1; row <= m; row += 1) //For each row on left matrix for(var/col = 1; col <= p; col += 1) //go over each column of the second matrix var/sum = 0 @@ -144,3 +355,8 @@ result[(row-1)*p + col] = sum return result + + +#undef LUMA_R +#undef LUMA_G +#undef LUMA_B diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm index 1399423c65f..1a151529119 100644 --- a/code/modules/orbit/orbit.dm +++ b/code/modules/orbit/orbit.dm @@ -57,13 +57,11 @@ //A: atom to orbit //radius: range to orbit at, radius of the circle formed by orbiting (in pixels) -//clockwise: whether you orbit clockwise or anti clockwise //rotation_speed: how fast to rotate (how many ds should it take for a rotation to complete) -//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default. -//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts +//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir, useful for things to go "head first" like ghosts //lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts) -/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE) +/atom/movable/proc/orbit(atom/A, radius = 10, rotation_speed = 20, pre_rotation = TRUE, lockinorbit = FALSE) if (!istype(A)) return @@ -75,17 +73,14 @@ //Head first! if (pre_rotation) var/matrix/M = matrix(transform) - var/pre_rot = 90 - if(!clockwise) - pre_rot = -90 - M.Turn(pre_rot) + M.Turn(90) transform = M var/matrix/shift = matrix(transform) shift.Translate(0,radius) transform = shift - SpinAnimation(rotation_speed, -1, clockwise, rotation_segments) + SpinAnimation(rotation_speed, -1) //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit transform = initial_transform diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index a163348eb38..3dd92b239b6 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -196,7 +196,7 @@ var/orbitsize = (I.Width() + I.Height()) * pick(0.4, 0.5, 0.6, 0.7, 0.8) orbitsize -= (orbitsize / world.icon_size) * (world.icon_size * 0.25) - EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36)) + EB.orbit(src, orbitsize, rand(10, 25)) /obj/singularity/energy_ball/Collide(atom/A) diff --git a/html/changelogs/fluffyghost-sanermatrixprocs.yml b/html/changelogs/fluffyghost-sanermatrixprocs.yml new file mode 100644 index 00000000000..f8958c2d5fe --- /dev/null +++ b/html/changelogs/fluffyghost-sanermatrixprocs.yml @@ -0,0 +1,60 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - code_imp: "Ported the various matrix procs and defines from TG." + - code_imp: "Updated our snowflake procs with the Bay's current version." + - refactor: "Refactor of our orbit proc to align with the updated procs, tesla energy ball too."