From 163c84344b31d85decb3eec180ff3755cd41069a Mon Sep 17 00:00:00 2001 From: Remie Richards Date: Wed, 16 Dec 2015 17:45:26 +0000 Subject: [PATCH] Orbit improvements: * Orbit is now less reliant on Animate(), for most ghosts this means 36 calls to Animate, vs the previous INFINITE, for those of you with potato computers, this should ease the pain and crashing. * Orbits can now be something different to circles! * Ghosts Byond Members can now choose between orbits! (Circle, Triangle, Square, Hexagon) --- code/__DEFINES/misc.dm | 7 ++ code/__HELPERS/matrices.dm | 33 ++++++---- code/__HELPERS/unsorted.dm | 71 +++++++++++---------- code/modules/client/preferences.dm | 8 +++ code/modules/client/preferences_savefile.dm | 3 + code/modules/client/preferences_toggles.dm | 18 +++++- code/modules/mob/dead/observer/login.dm | 1 + code/modules/mob/dead/observer/observer.dm | 15 ++++- 8 files changed, 109 insertions(+), 47 deletions(-) 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() ..()