diff --git a/code/_onclick/hud/parallax/parallax.dm b/code/_onclick/hud/parallax/parallax.dm index 506226b41ea..bcdcd0e74fe 100644 --- a/code/_onclick/hud/parallax/parallax.dm +++ b/code/_onclick/hud/parallax/parallax.dm @@ -11,6 +11,10 @@ for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX)) parallax.unhide_plane(screenmob) + if(isnull(C.parallax_rock)) + C.parallax_rock = new(null, src) + C.screen |= C.parallax_rock + if(!length(C.parallax_layers_cached)) C.parallax_layers_cached = list() C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, src) @@ -25,7 +29,7 @@ if (length(C.parallax_layers) > C.parallax_layers_max) C.parallax_layers.len = C.parallax_layers_max - C.screen |= (C.parallax_layers) + C.parallax_rock.vis_contents = C.parallax_layers // We could do not do parallax for anything except the main plane group // This could be changed, but it would require refactoring this whole thing // And adding non client particular hooks for all the inputs, and I do not have the time I'm sorry :( @@ -44,7 +48,7 @@ /datum/hud/proc/remove_parallax(mob/viewmob) var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client - C.screen -= (C.parallax_layers_cached) + C.screen -= (C.parallax_rock) for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE)) if(screenmob != mymob) C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen @@ -104,83 +108,65 @@ update_parallax(screen_mob) // This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation) -/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = 0, skip_windups, mob/viewmob) +/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = NONE, skip_windups, mob/viewmob) . = FALSE var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client if(new_parallax_movedir == C.parallax_movedir) return - var/animatedir = new_parallax_movedir - if(new_parallax_movedir == FALSE) - var/animate_time = 0 - for(var/thing in C.parallax_layers) - var/atom/movable/screen/parallax_layer/L = thing - L.icon_state = initial(L.icon_state) - L.update_o(C.view) - var/T = PARALLAX_LOOP_TIME / L.speed - if (T > animate_time) - animate_time = T - C.dont_animate_parallax = world.time + min(animate_time, PARALLAX_LOOP_TIME) - animatedir = C.parallax_movedir - var/matrix/newtransform - switch(animatedir) + var/animation_dir = new_parallax_movedir || C.parallax_movedir + var/matrix/new_transform + switch(animation_dir) if(NORTH) - newtransform = matrix(1, 0, 0, 0, 1, 480) + new_transform = matrix(1, 0, 0, 0, 1, 480) if(SOUTH) - newtransform = matrix(1, 0, 0, 0, 1,-480) + new_transform = matrix(1, 0, 0, 0, 1,-480) if(EAST) - newtransform = matrix(1, 0, 480, 0, 1, 0) + new_transform = matrix(1, 0, 480, 0, 1, 0) if(WEST) - newtransform = matrix(1, 0,-480, 0, 1, 0) + new_transform = matrix(1, 0,-480, 0, 1, 0) - var/shortesttimer - if(!skip_windups) - for(var/thing in C.parallax_layers) - var/atom/movable/screen/parallax_layer/L = thing + var/longest_timer = 0 + for(var/key in C.parallax_animate_timers) + deltimer(C.parallax_animate_timers[key]) + C.parallax_animate_timers = list() + for(var/atom/movable/screen/parallax_layer/layer as anything in C.parallax_layers) + var/scaled_time = PARALLAX_LOOP_TIME / layer.speed + if(new_parallax_movedir == NONE) // If we're stopping, we need to stop on the same dime, yeah? + scaled_time = PARALLAX_LOOP_TIME + longest_timer = max(longest_timer, scaled_time) - var/T = PARALLAX_LOOP_TIME / L.speed - if (isnull(shortesttimer)) - shortesttimer = T - if (T < shortesttimer) - shortesttimer = T - L.transform = newtransform - animate(L, transform = matrix(), time = T, easing = QUAD_EASING | (new_parallax_movedir ? EASE_IN : EASE_OUT), flags = ANIMATION_END_NOW) - if (new_parallax_movedir) - L.transform = newtransform - animate(transform = matrix(), time = T) //queue up another animate so lag doesn't create a shutter - - C.parallax_movedir = new_parallax_movedir - if (C.parallax_animate_timer) - deltimer(C.parallax_animate_timer) - var/datum/callback/CB = CALLBACK(src, PROC_REF(update_parallax_motionblur), C, animatedir, new_parallax_movedir, newtransform) - if(skip_windups) - CB.Invoke() - else - C.parallax_animate_timer = addtimer(CB, min(shortesttimer, PARALLAX_LOOP_TIME), TIMER_CLIENT_TIME|TIMER_STOPPABLE) - - -/datum/hud/proc/update_parallax_motionblur(client/C, animatedir, new_parallax_movedir, matrix/newtransform) - if(!C) - return - C.parallax_animate_timer = FALSE - for(var/thing in C.parallax_layers) - var/atom/movable/screen/parallax_layer/L = thing - if (!new_parallax_movedir) - animate(L) + if(skip_windups) + update_parallax_motionblur(C, layer, new_parallax_movedir, new_transform) continue - var/newstate = initial(L.icon_state) - var/T = PARALLAX_LOOP_TIME / L.speed + layer.transform = new_transform + animate(layer, transform = matrix(), time = scaled_time, easing = QUAD_EASING | (new_parallax_movedir ? EASE_IN : EASE_OUT)) + if (new_parallax_movedir == NONE) + continue + //queue up another animate so lag doesn't create a shutter + animate(transform = new_transform, time = 0) + animate(transform = matrix(), time = scaled_time / 2) + C.parallax_animate_timers[layer] = addtimer(CALLBACK(src, PROC_REF(update_parallax_motionblur), C, layer, new_parallax_movedir, new_transform), scaled_time, TIMER_CLIENT_TIME|TIMER_STOPPABLE) - if (newstate in icon_states(L.icon)) - L.icon_state = newstate - L.update_o(C.view) + C.dont_animate_parallax = world.time + min(longest_timer, PARALLAX_LOOP_TIME) + C.parallax_movedir = new_parallax_movedir - L.transform = newtransform +/datum/hud/proc/update_parallax_motionblur(client/C, atom/movable/screen/parallax_layer/layer, new_parallax_movedir, matrix/new_transform) + if(!C) + return + C.parallax_animate_timers -= layer - animate(L, transform = L.transform, time = 0, loop = -1, flags = ANIMATION_END_NOW) - animate(transform = matrix(), time = T) + // If we are moving in a direction, we used the QUAD_EASING function with EASE_IN + // This means our position function is x^2. This is always LESS then the linear we're using here + // But if we just used the same time delay, our rate of change would mismatch. f'(1) = 2x for quad easing, rather then the 1 we get for linear + // (This is because of how derivatives work right?) + // Because of this, while our actual rate of change from before was PARALLAX_LOOP_TIME, our perceived rate of change was PARALLAX_LOOP_TIME / 2 (lower == faster). + // Let's account for that here + var/scaled_time = (PARALLAX_LOOP_TIME / layer.speed) / 2 + animate(layer, transform = new_transform, time = 0, loop = -1, flags = ANIMATION_END_NOW) + animate(transform = matrix(), time = scaled_time) /datum/hud/proc/update_parallax(mob/viewmob) var/mob/screenmob = viewmob || mymob @@ -217,36 +203,41 @@ var/our_speed = parallax_layer.speed var/change_x var/change_y + var/old_x = parallax_layer.offset_x + var/old_y = parallax_layer.offset_y if(parallax_layer.absolute) // We use change here so the typically large absolute objects (just lavaland for now) don't jitter so much - change_x = (posobj.x - SSparallax.planet_x_offset) * our_speed + parallax_layer.offset_x - change_y = (posobj.y - SSparallax.planet_y_offset) * our_speed + parallax_layer.offset_y + change_x = (posobj.x - SSparallax.planet_x_offset) * our_speed + old_x + change_y = (posobj.y - SSparallax.planet_y_offset) * our_speed + old_y else change_x = offset_x * our_speed change_y = offset_y * our_speed // This is how we tile parralax sprites // It doesn't use change because we really don't want to animate this - if(parallax_layer.offset_x - change_x > 240) + if(old_x - change_x > 240) parallax_layer.offset_x -= 480 - else if(parallax_layer.offset_x - change_x < -240) + parallax_layer.pixel_w = parallax_layer.offset_x + else if(old_x - change_x < -240) parallax_layer.offset_x += 480 - if(parallax_layer.offset_y - change_y > 240) + parallax_layer.pixel_w = parallax_layer.offset_x + if(old_y - change_y > 240) parallax_layer.offset_y -= 480 - else if(parallax_layer.offset_y - change_y < -240) + parallax_layer.pixel_z = parallax_layer.offset_y + else if(old_y - change_y < -240) parallax_layer.offset_y += 480 + parallax_layer.pixel_z = parallax_layer.offset_y - // Now that we have our offsets, let's do our positioning parallax_layer.offset_x -= change_x parallax_layer.offset_y -= change_y - - parallax_layer.screen_loc = "CENTER-7:[round(parallax_layer.offset_x, 1)],CENTER-7:[round(parallax_layer.offset_y, 1)]" - - // We're going to use a transform to "glide" that last movement out, so it looks nicer + // Now that we have our offsets, let's do our positioning + // We're going to use an animate to "glide" that last movement out, so it looks nicer // Don't do any animates if we're not actually moving enough distance yeah? thanks lad if(run_parralax && (largest_change * our_speed > 1)) - parallax_layer.transform = matrix(1,0,change_x, 0,1,change_y) - animate(parallax_layer, transform=matrix(), time = glide_rate) + animate(parallax_layer, pixel_w = round(parallax_layer.offset_x, 1), pixel_z = round(parallax_layer.offset_y, 1), time = glide_rate) + else + parallax_layer.pixel_w = round(parallax_layer.offset_x, 1) + parallax_layer.pixel_z = round(parallax_layer.offset_y, 1) /atom/movable/proc/update_parallax_contents() for(var/mob/client_mob as anything in client_mobs_in_contents) @@ -258,6 +249,15 @@ var/area/areaobj = get_area(client.eye) hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE) +// Root object for parallax, all parallax layers are drawn onto this +INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home) +/atom/movable/screen/parallax_home + icon = null + blend_mode = BLEND_ADD + plane = PLANE_SPACE_PARALLAX + screen_loc = "CENTER-7,CENTER-7" + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + // We need parallax to always pass its args down into initialize, so we immediate init it INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer) /atom/movable/screen/parallax_layer @@ -266,9 +266,9 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer) var/offset_x = 0 var/offset_y = 0 var/absolute = FALSE + appearance_flags = APPEARANCE_UI | KEEP_TOGETHER blend_mode = BLEND_ADD plane = PLANE_SPACE_PARALLAX - screen_loc = "CENTER-7,CENTER-7" mouse_opacity = MOUSE_OPACITY_TRANSPARENT /atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, template = FALSE) @@ -302,15 +302,17 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer) // Turn the view size into a grid of correctly scaled overlays var/list/viewscales = getviewsize(view) - var/countx = CEILING((viewscales[1] / 2) * parallax_scaler, 1) + 1 - var/county = CEILING((viewscales[2] / 2) * parallax_scaler, 1) + 1 + // This could be half the size but we need to provide space for parallax movement on mob movement, and movement on scroll from shuttles, so like this instead + var/countx = (CEILING((viewscales[1] / 2) * parallax_scaler, 1) + 1) + var/county = (CEILING((viewscales[2] / 2) * parallax_scaler, 1) + 1) var/list/new_overlays = new for(var/x in -countx to countx) for(var/y in -county to county) if(x == 0 && y == 0) continue var/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state) - texture_overlay.transform = matrix(1, 0, x*480, 0, 1, y*480) + texture_overlay.pixel_w += 480 * x + texture_overlay.pixel_z += 480 * y new_overlays += texture_overlay cut_overlays() add_overlay(new_overlays) diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 3272620a865..97924643604 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -197,6 +197,7 @@ var/list/parallax_layers var/list/parallax_layers_cached + var/atom/movable/screen/parallax_home/parallax_rock ///this is the last recorded client eye by SSparallax/fire() var/atom/movable/movingmob var/turf/previous_turf @@ -206,8 +207,8 @@ var/parallax_movedir = 0 /// How many parallax layers to show our client var/parallax_layers_max = 4 - /// Timer for the area directional animation - var/parallax_animate_timer + /// Timers for the area directional animation, one for each layer + var/list/parallax_animate_timers /// Do we want to do parallax animations at all? /// Exists to prevent laptop fires var/do_parallax_animations = TRUE diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index c3163b3e0b1..1de6c330bfc 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -601,6 +601,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( QDEL_NULL(void) QDEL_NULL(tooltips) QDEL_NULL(loot_panel) + QDEL_NULL(parallax_rock) + QDEL_LIST(parallax_layers_cached) + parallax_layers = null seen_messages = null Master.UpdateTickRate() ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening