diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index f6b088bbe2c..a15e44bf499 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -310,3 +310,10 @@ var/list/bloody_footprints_cache = list() #define FIRE_PROOF -1 #define FLAMMABLE 0 #define ON_FIRE 1 + + +//Ghost orbit types: +#define GHOST_ORBIT_CIRCLE "circle" +#define GHOST_ORBIT_TRIANGLE "triangle" +#define GHOST_ORBIT_HEXAGON "hexagon" +#define GHOST_ORBIT_SQUARE "square" diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index ead70a299a6..17228a32fe4 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -3,15 +3,24 @@ Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT -/atom/proc/SpinAnimation(speed = 10, loops = -1) - var/matrix/m120 = matrix(transform) - m120.Turn(120) - var/matrix/m240 = matrix(transform) - m240.Turn(240) - var/matrix/m360 = matrix(transform) - speed /= 3 //Gives us 3 equal time segments for our three turns. - //Why not one turn? Because byond will see that the start and finish are the same place and do nothing - //Why not two turns? Because byond will do a flip instead of a turn - animate(src, transform = m120, time = speed, loops) - animate(transform = m240, time = speed) - animate(transform = m360, time = speed) \ No newline at end of file +/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 + + speed /= segments + + 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 diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 51afdf13ff6..8e6afc688e3 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1192,51 +1192,56 @@ B --><-- A //orbit() can run without it (swap orbiting for A) //but then you can never stop it and that's just silly. /atom/movable/var/atom/orbiting = null -//we raise this each time orbit is called to prevent mutiple calls in a short time frame from breaking things -/atom/movable/var/orbitid = 0 -/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = 1, angle_increment = 15, lockinorbit = 0) +//A: atom to orbit +//radius: range to orbit at, radius of the circle formed by orbiting +//clockwise: whether you orbit clockwise or anti clockwise +//rotation_speed: how fast to rotate +//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 +//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) if(!istype(A)) return - orbitid++ - var/myid = orbitid - if (orbiting) - stop_orbit() - //sadly this is the only way to ensure the original orbit proc stops - //and resets the atom's transform before we continue. - //time is based on the sleep in the loop and the time for the final animation of initial_transform. - sleep(2.6+world.tick_lag) - if (orbiting || !istype(A) || orbitid != myid) //post sleep re-check - return - orbiting = A - var/lastloc = loc - var/angle = 0 - var/matrix/initial_transform = matrix(transform) - while(orbiting && orbiting.loc && orbitid == myid) + if(orbiting) + stop_orbit() + sleep(2.6+world.tick_lag) //the 2 second delay at the end of the existing orbit() call, plus some lag slack. + + orbiting = A + var/matrix/initial_transform = matrix(transform) + var/lastloc = loc + + //Head first! + if(pre_rotation) + var/matrix/M = matrix(transform) + var/pre_rot = 90 + if(!clockwise) + pre_rot = -90 + M.Turn(pre_rot) + transform = M + + var/matrix/shift = matrix(transform) + shift.Translate(0,radius) + transform = shift + + SpinAnimation(rotation_speed, -1, clockwise, rotation_segments) + while(orbiting && orbiting.loc) var/targetloc = get_turf(orbiting) - if (!lockinorbit && loc != lastloc && loc != targetloc) + if(!lockinorbit && loc != lastloc && loc != targetloc) break loc = targetloc lastloc = loc - angle += angle_increment + sleep(0.6) + + animate(src,transform = initial_transform, time = 2) //2 second delay + SpinAnimation(0,0) - var/matrix/shift = matrix(initial_transform) - shift.Translate(radius,0) - if(clockwise) - shift.Turn(angle) - else - shift.Turn(-angle) - animate(src, transform = shift, 2) - sleep(0.6) //the effect breaks above 0.6 delay - animate(src, transform = initial_transform, 2) - orbiting = null /atom/movable/proc/stop_orbit() - if(orbiting) - loc = get_turf(orbiting) - orbiting = null + orbiting = null //Center's an image. diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index b1925cabe91..c001e766f7a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -31,6 +31,7 @@ var/list/preferences_datums = list() var/toggles = TOGGLES_DEFAULT var/chat_toggles = TOGGLES_DEFAULT_CHAT var/ghost_form = "ghost" + var/ghost_orbit = GHOST_ORBIT_CIRCLE var/allow_midround_antag = 1 var/preferred_map = null @@ -346,6 +347,7 @@ var/list/preferences_datums = list() if(unlock_content) dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" dat += "Ghost Form: [ghost_form]
" + dat += "Ghost Orbit: [ghost_orbit]
" if (SERVERTOOLS && config.maprotation) var/p_map = preferred_map @@ -734,6 +736,12 @@ var/list/preferences_datums = list() var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in ghost_forms if(new_form) ghost_form = new_form + if("ghostorbit") + if(unlock_content) + var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in ghost_orbits + if(new_orbit) + ghost_orbit = new_orbit + if("name") var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) if(new_name) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 5abcbd7b972..b5d4f5c11f3 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -178,6 +178,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["chat_toggles"] >> chat_toggles S["toggles"] >> toggles S["ghost_form"] >> ghost_form + S["ghost_orbit"] >> ghost_orbit S["preferred_map"] >> preferred_map S["ignoring"] >> ignoring @@ -194,6 +195,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) toggles = sanitize_integer(toggles, 0, 65535, initial(toggles)) ghost_form = sanitize_inlist(ghost_form, ghost_forms, initial(ghost_form)) + ghost_orbit = sanitize_inlist(ghost_orbit, ghost_orbits, initial(ghost_orbit)) return 1 @@ -215,6 +217,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["toggles"] << toggles S["chat_toggles"] << chat_toggles S["ghost_form"] << ghost_form + S["ghost_orbit"] << ghost_orbit S["preferred_map"] << preferred_map S["ignoring"] << ignoring diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 4f944500504..77643a99617 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -205,7 +205,7 @@ src.ambience_playing = 0 feedback_add_details("admin_verb", "SAmbi") //If you are copy-pasting this, I bet you read this comment expecting to see the same thing :^) -var/list/ghost_forms = list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \ +var/global/list/ghost_forms = list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \ "ghost_blue","ghost_yellow","ghost_green","ghost_pink", \ "ghost_cyan","ghost_dblue","ghost_dred","ghost_dgreen", \ "ghost_dcyan","ghost_grey","ghost_dyellow","ghost_dpink", "ghost_purpleswirl","ghost_funkypurp","ghost_pinksherbert","ghost_blazeit",\ @@ -222,6 +222,22 @@ var/list/ghost_forms = list("ghost","ghostking","ghostian2","skeleghost","ghost_ if(istype(mob,/mob/dead/observer)) mob.icon_state = new_form +var/global/list/ghost_orbits = list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOST_ORBIT_SQUARE,GHOST_ORBIT_HEXAGON) + +/client/verb/pick_ghost_orbit() + set name = "Choose Ghost Orbit" + set category = "Preferences" + set desc = "Choose your preferred ghostly orbit." + if(!is_content_unlocked()) + return + var/new_orbit = input(src, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND",null) as null|anything in ghost_orbits + if(new_orbit) + prefs.ghost_orbit = new_orbit + prefs.save_preferences() + if(istype(mob, /mob/dead/observer)) + var/mob/dead/observer/O = mob + O.ghost_orbit = new_orbit + /client/verb/toggle_intent_style() set name = "Toggle Intent Selection Style" set category = "Preferences" diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 76dac151c11..5a97bdceed7 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -8,6 +8,7 @@ icon_state = client.prefs.ghost_form if (ghostimage) ghostimage.icon_state = src.icon_state + ghost_orbit = client.prefs.ghost_orbit updateghostimages() diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 350ea9720d0..e9b2f58dfc0 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -24,6 +24,7 @@ var/list/image/ghost_darkness_images = list() //this is a list of images for thi var/seedarkness = 1 var/ghost_hud_enabled = 1 //did this ghost disable the on-screen HUD? var/data_hud_seen = 0 //this should one of the defines in __DEFINES/hud.dm + var/ghost_orbit = GHOST_ORBIT_CIRCLE /mob/dead/observer/New(mob/body) sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF @@ -225,7 +226,19 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(orbiting != target) src << "Now orbiting [target]." - orbit(target,orbitsize,0) + var/rot_seg + + switch(ghost_orbit) + if(GHOST_ORBIT_TRIANGLE) + rot_seg = 3 + if(GHOST_ORBIT_SQUARE) + rot_seg = 4 + if(GHOST_ORBIT_HEXAGON) + rot_seg = 6 + else //Circular + rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle + + orbit(target,orbitsize, FALSE, 20, rot_seg) /mob/dead/observer/orbit() ..()