Cargo elevator overhaul (#21430)

## About PR

Implements an animation logic heavily inspired from CM-SS13 elevators.
Elevator hatch and shaft sprites are courtesy of @KingOfThePing!

Currently if something is standing on the hatches when the elevator is
arriving, they'll be sent to CC level, fall, get paralyzed and severely
injured enough to have broken bones/arterial bleeding.

This PR also fixes cargo announcement screens not displaying elevator
ETA.

## Images

<details><summary>Elevator arriving</summary>
<p>



https://github.com/user-attachments/assets/34879994-d10b-4cb6-959e-314ec20411b8

</p>
</details> 

<details><summary>Elevator departing</summary>
<p>


https://github.com/user-attachments/assets/c345fd3b-07eb-4922-9b28-31cf40842da1

</p>
</details> 

<details><summary>Unfortunate victims falling to the pit</summary>
<p>



https://github.com/user-attachments/assets/98feac04-5d65-4383-8abc-363ce51a028a


</p>
</details> 

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/effect/224x192.dmi | KingOfThePing | CC BY-SA 3.0 |
| icons/obj/doors/retractable_railing.dmi | CM-SS13 | CC BY-SA 3.0 |
| sound/machines/industrial_lift_raising.ogg | CM-SS13 | CC BY-SA 3.0 |
| sound/machines/industrial_lift_lowering.ogg | CM-SS13 | CC BY-SA 3.0 |
| sound/machines/warning-buzzer-2.ogg | Unknown -
https://pixabay.com/sound-effects/incorrect-buzzer-sound-147336/ |
Pixabay Licence |
This commit is contained in:
Kano
2025-10-05 14:26:49 +00:00
committed by GitHub
parent 451eb5dcef
commit 2d9d4ce4b6
15 changed files with 1078 additions and 543 deletions
+8 -11
View File
@@ -107,14 +107,11 @@
if(on)
vis_contents += spin_effect
/obj/machinery/rotating_alarm/proc/set_on()
vis_contents += spin_effect
set_light(2, 0.5, alarm_light_color)
on = TRUE
/obj/machinery/rotating_alarm/proc/set_off()
vis_contents -= spin_effect
set_light(0)
on = FALSE
/obj/machinery/rotating_alarm/proc/toggle_state()
if(on)
vis_contents -= spin_effect
set_light(0)
else
vis_contents += spin_effect
set_light(2, 0.5, alarm_light_color)
on = !on
+22 -36
View File
@@ -119,24 +119,7 @@
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
return 1
if(STATUS_DISPLAY_MESSAGE) //custom messages
var/line1_metric
var/line2_metric
var/line_pair
var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM()
line1_metric = display_font.get_metrics(message1)
line2_metric = display_font.get_metrics(message2)
line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric)
var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair)
if(overlay)
message1_overlay = overlay
overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair)
if(overlay)
message2_overlay = overlay
if(message1 == "" && message2 == "")
return 1
else
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
return 1
arrange_displayed_texts(message1, message2)
if(STATUS_DISPLAY_ALERT)
set_picture(picture_state)
return 1
@@ -144,26 +127,29 @@
message1 = "-Time-"
message2 = worldtime2text()
set_messages(message1, message2)
var/line1_metric
var/line2_metric
var/line_pair
var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM()
line1_metric = display_font.get_metrics(message1)
line2_metric = display_font.get_metrics(message2)
line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric)
var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair)
if(overlay)
message1_overlay = overlay
overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair)
if(overlay)
message2_overlay = overlay
if(message1 == "" && message2 == "")
return 1
else
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
return 1
arrange_displayed_texts(message1, message2)
return 0
/obj/machinery/status_display/proc/arrange_displayed_texts(message1, message2)
var/line1_metric
var/line2_metric
var/line_pair
var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM()
line1_metric = display_font.get_metrics(message1)
line2_metric = display_font.get_metrics(message2)
line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric)
var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair)
if(overlay)
message1_overlay = overlay
overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair)
if(overlay)
message2_overlay = overlay
if(message1 == "" && message2 == "")
return 1
else
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
return 1
/obj/machinery/status_display/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT)
@@ -20,11 +20,11 @@
message2 = "ETA"
else
if(shuttle.at_station())
message2 = "Docked"
message2 = "Elevator Arrived"
else
message1 = ""
set_messages(message1, message2)
return 1
arrange_displayed_texts(message1, message2)
return 0
/obj/machinery/status_display/supply_display/receive_signal(datum/signal/signal)
+31 -3
View File
@@ -22,9 +22,8 @@
// Cargo Elevator Plating
/turf/simulated/floor/plating/cargo_elevator
name = "cargo elevator plating"
desc = "The cargo elevator's plating."
mouse_opacity = MOUSE_OPACITY_TRANSPARENT // Obscured by the hatch, shouldn't be examinable by players.
name = "cargo hatch plating"
desc = "The cargo hatch's plating."
// Sanity checks in case someone somehow tries to interact with one of the platings.
/turf/simulated/floor/plating/cargo_elevator/attack_hand(mob/user)
@@ -32,3 +31,32 @@
/turf/simulated/floor/plating/cargo_elevator/attackby(obj/item/attacking_item, mob/user)
to_chat(user, SPAN_NOTICE("You hit \the [src] with \the [attacking_item]. Nothing happens."))
/obj/effect/elevator
name = "\proper elevator shaft"
desc = "There seems to be an awful lot of machinery down below."
icon = 'icons/effects/224x192.dmi'
icon_state = "elevator_shaft"
unacidable = TRUE
layer = TURF_DETAIL_LAYER
appearance_flags = KEEP_TOGETHER
/obj/effect/elevator/ex_act(severity)
return
/obj/effect/elevator/animation_overlay
icon_state = null
layer = TURF_SHADOW_LAYER
blend_mode = BLEND_INSET_OVERLAY
appearance_flags = KEEP_TOGETHER
/obj/effect/elevator/animation_overlay/hatch
name = "elevator hatch"
icon_state = "hatch"
/obj/effect/elevator/animation_overlay/hatch/left
icon_state = "hatch_L"
/obj/effect/elevator/animation_overlay/hatch/right
icon_state = "hatch_R"
+87 -11
View File
@@ -18,6 +18,11 @@
var/maxhealth = 70
var/neighbor_status = 0
/// If the object shouldn't inherit a material, set this to True.
var/non_material_object = FALSE
var/can_wrench = TRUE
var/can_screwdriver = TRUE
can_astar_pass = CANASTARPASS_ALWAYS_PROC
/obj/structure/railing/condition_hints(mob/user, distance, is_adjacent)
@@ -72,19 +77,20 @@
/obj/structure/railing/Initialize()
. = ..()
if(!isnull(material) && !istype(material))
material = SSmaterials.get_material_by_name(material)
if(!istype(material))
return INITIALIZE_HINT_QDEL
if(!non_material_object)
if(!isnull(material) && !istype(material))
material = SSmaterials.get_material_by_name(material)
if(!istype(material))
return INITIALIZE_HINT_QDEL
name = "[material.display_name] [initial(name)]"
desc = "A simple [material.display_name] railing designed to protect against careless trespass."
maxhealth = round(material.integrity / 5)
health = maxhealth
color = material.icon_colour
name = "[material.display_name] [initial(name)]"
desc = "A simple [material.display_name] railing designed to protect against careless trespass."
maxhealth = round(material.integrity / 5)
health = maxhealth
color = material.icon_colour
if(material.products_need_process())
START_PROCESSING(SSprocessing, src)
if(material.products_need_process())
START_PROCESSING(SSprocessing, src)
if(anchored)
update_icon(FALSE)
@@ -246,6 +252,9 @@
// Dismantle
if(attacking_item.iswrench())
if(!can_wrench)
to_chat(user, SPAN_WARNING("This [src] cannot be adjusted."))
return
if(!anchored)
user.visible_message(SPAN_NOTICE("\The [user] starts dismantling \the [src]..."), SPAN_NOTICE("You start dismantling \the [src]..."))
if(attacking_item.use_tool(src, user, 20, volume = 50))
@@ -283,6 +292,9 @@
// Install
if(attacking_item.isscrewdriver())
if(!can_screwdriver)
to_chat(user, SPAN_WARNING("This [src] cannot be adjusted."))
return
if(!density)
to_chat(user, SPAN_NOTICE("You need to wrench \the [src] from back into place first."))
return
@@ -365,3 +377,67 @@
/obj/structure/railing/fence/New(var/newloc, var/material_key = MATERIAL_WOOD)
material = material_key
..(newloc)
/obj/structure/railing/retractable
name = "\improper retractable railing"
icon = 'icons/obj/doors/retractable_railing.dmi'
icon_state = "railing1"
anchored = TRUE
health = 150
maxhealth = 150
non_material_object = TRUE
can_wrench = FALSE
can_screwdriver = FALSE
var/closed_layer = ABOVE_DOOR_LAYER
var/open_layer = OPEN_DOOR_LAYER
var/icon_state_open = "railing0"
var/icon_state_opening = "railingc0"
var/icon_state_closed = "railing1"
var/icon_state_closing = "railingc1"
var/operating
/obj/structure/railing/retractable/Initialize()
. = ..()
if(dir == SOUTH)
closed_layer = ABOVE_HUMAN_LAYER
if(density)//Allows preset-open to work
layer = closed_layer
set_opacity(initial(opacity))
/obj/structure/railing/retractable/update_icon()
if(density)
icon_state = icon_state_closed
else
icon_state = icon_state_open
/obj/structure/railing/retractable/proc/toggle_state()
if(operating)
return
operating = TRUE
flick(density ? icon_state_opening : icon_state_closing, src)
playsound(get_turf(src), 'sound/machines/retractable_railing_openclose.ogg', 20)
if(density)
icon_state = icon_state_open
layer = open_layer
else
icon_state = icon_state_closed
layer = closed_layer
addtimer(CALLBACK(src, PROC_REF(finish_toggling)), 1.2 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_NO_HASH_WAIT)
return TRUE
/obj/structure/railing/retractable/proc/finish_toggling()
density = !density
operating = FALSE
/obj/structure/railing/retractable/NeighborsCheck()
return
/obj/structure/railing/retractable/flip()
return
/obj/structure/railing/retractable/open
density = FALSE
+1
View File
@@ -351,6 +351,7 @@
icon_state = "stairs_railing"
anchored = TRUE
density = TRUE
layer = ABOVE_ABOVE_HUMAN_LAYER
/obj/structure/stairs_railing/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(mover?.movement_type & PHASING)
+239 -2
View File
@@ -1,12 +1,84 @@
/// Used for placing '/obj/effect/elevator' to the northern corners of the shaft.
#define SHAFT_WIDTH 6
/// Used for locating center of the shaft.
#define SHAFT_CENTER_OFFSET 3
#define ELEVATOR_DEPARTING_X 224
#define ELEVATOR_DEPARTING_Y -96
/datum/shuttle/autodock/ferry/supply
category = /datum/shuttle/autodock/ferry/supply
var/away_location = 1 //the location to hide at while pretending to be in-transit
/// The location to hide at while pretending to be in-transit.
var/away_location = 1
var/late_chance = 80
var/max_late_time = 300
/// A list for step_triggers, used for toggling fall functionality. Making the elevator shaft tiles safe to stand or not depening on where the elevator is.
var/list/step_trigger_group = list()
/// Elevator effects for each northern corners of the shaft. This way the entire elevator won't disappear if there's an opaque obstacle in the sight line of original turf.
/// Later on hardcoded to have their icons positioned on top of each other.
/// These contain elevator shaft sprite. '/obj/effect/elevator/animation_overlay/elevator_animation' is used as a masked layer onto this.
var/obj/effect/elevator/NW
var/obj/effect/elevator/NE
/// This effect will contain an image, copy of the turfs at CC supply shuttle zone. Added onto the elevator shaft effect as an off-centered overlay.
var/obj/effect/elevator/animation_overlay/elevator_animation
/// Hatch layers. Works similar to 'elevator_animation'.
var/obj/effect/elevator/animation_overlay/hatch/left/hatch_left
var/obj/effect/elevator/animation_overlay/hatch/right/hatch_right
/// Locations caches for Horizon elevator bay.
var/target_dest_x
var/target_dest_y
var/target_dest_z
/// Area cache of Horizon elevator bay.
var/area/horizon_elevator_area
/datum/shuttle/autodock/ferry/supply/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
SScargo.shuttle = src
addtimer(CALLBACK(src, PROC_REF(prepare_elevator)), 1 MINUTE) // pseudo initialize. We give mapload some time before initializing rest of the properties
/datum/shuttle/autodock/ferry/supply/proc/prepare_elevator()
var/obj/dest_helper = locate(/obj/effect/landmark/destination_helper/cargo_elevator)
target_dest_x = dest_helper.x
target_dest_y = dest_helper.y
target_dest_z = dest_helper.z
horizon_elevator_area = get_area(dest_helper)
var/center_dest_x = target_dest_x + SHAFT_CENTER_OFFSET
var/group_number
for(var/obj/effect/step_trigger/cargo_elevator/ST in horizon_elevator_area) // sorts step triggers by their distance to the shaft center
group_number = "distance_[abs(ST.x - center_dest_x)]"
LAZYADDASSOCLIST(step_trigger_group, group_number, ST)
elevator_animation = new /obj/effect/elevator/animation_overlay()
elevator_animation.pixel_x = ELEVATOR_DEPARTING_X // 7 tiles
elevator_animation.pixel_y = ELEVATOR_DEPARTING_Y // 3 tiles
hatch_left = new /obj/effect/elevator/animation_overlay/hatch/left()
hatch_right = new /obj/effect/elevator/animation_overlay/hatch/right()
// North-West corner
NW = new /obj/effect/elevator(get_turf(src))
NW.pixel_y = -192 // 6 tiles
NW.vis_contents += elevator_animation
NW.vis_contents += hatch_left
NW.vis_contents += hatch_right
// North-East corner
NE = new /obj/effect/elevator(get_turf(src))
NE.pixel_x = -192
NE.pixel_y = -192
NE.vis_contents += elevator_animation
NE.vis_contents += hatch_left
NE.vis_contents += hatch_right
// We position the shaft beforehand
NW.forceMove(locate(target_dest_x, target_dest_y, target_dest_z))
NE.forceMove(locate(target_dest_x + SHAFT_WIDTH, target_dest_y, target_dest_z))
/datum/shuttle/autodock/ferry/supply/short_jump(var/area/destination)
if(moving_status != SHUTTLE_IDLE)
@@ -26,9 +98,13 @@
moving_status = SHUTTLE_IDLE
return
if (!at_station()) //at centcom
if (!at_station()) //at centcom
if(!SScargo.buy()) //Check if the shuttle can be sent
moving_status = SHUTTLE_IDLE //Dont move the shuttle
return
flip_rotating_alarms() // elevator is coming up, prepare the lights
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/warning-buzzer-2.ogg', 60, FALSE)
//We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period.
var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location)
@@ -37,6 +113,7 @@
//If we are at the away_landmark then we are just pretending to move, otherwise actually do the move
if (next_location == away_waypoint)
attempt_move(away_waypoint)
play_elevator_animation(TRUE) // returning to CC, play the animation after elevator physically leaves
//wait ETA here.
arrive_time = world.time + SScargo.movetime
@@ -48,6 +125,7 @@
if (prob(late_chance))
sleep(rand(0,max_late_time))
play_elevator_animation() // coming from CC, play the animation before elevator physically arrives, delay the process
attempt_move(destination)
moving_status = SHUTTLE_IDLE
@@ -75,3 +153,162 @@
/datum/shuttle/autodock/ferry/supply/proc/eta_minutes()
var/ticksleft = arrive_time - world.time
return round(ticksleft/600,1)
/**************************
Elevator Animations
***************************/
/datum/shuttle/autodock/ferry/supply/proc/play_elevator_animation(returning_to_CC = FALSE)
// Positioning the shafts
NW.forceMove(locate(target_dest_x, target_dest_y, target_dest_z))
NE.forceMove(locate(target_dest_x + SHAFT_WIDTH, target_dest_y, target_dest_z))
for(var/area/A in shuttle_area) // an image copy of the elevator platform is prepared here
for(var/turf/T in A)
elevator_animation.vis_contents += T
if(!returning_to_CC)
handle_arrival_sequence() // coming to Horizon
else
handle_departure_sequence() // leaving the Horizon
elevator_animation.vis_contents.Cut() // animation is done, we can get rid of the elevator platform image
/datum/shuttle/autodock/ferry/supply/proc/handle_arrival_sequence()
var/obj/effect/step_trigger/cargo_elevator/trigger
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/industrial_lift_raising.ogg', 100, FALSE)
INVOKE_ASYNC(src, PROC_REF(handle_step_triggers))
animate(hatch_left, pixel_x = -112, time = 5 SECONDS)
animate(hatch_right, pixel_x = 112, time = 5 SECONDS)
sleep(4 SECONDS)
animate(elevator_animation, pixel_x = 0, pixel_y = 0, time = 4 SECONDS)
sleep(4.2 SECONDS)
for(var/group_key in step_trigger_group)
for(trigger in step_trigger_group[group_key])
trigger.safe_to_walk = TRUE
// We move the shafts away to avoid layering issues with turfs
NW.moveToNullspace()
NE.moveToNullspace()
addtimer(CALLBACK(src, PROC_REF(flip_rotating_alarms)), 4 SECONDS)
addtimer(CALLBACK(src, PROC_REF(toggle_railings)), 2 SECONDS)
/datum/shuttle/autodock/ferry/supply/proc/handle_departure_sequence()
flip_rotating_alarms()
toggle_railings()
sleep(2 SECONDS)
// checking for standers is redundant here, since the elevator won't leave if a forbidden type is standing
// we also want to avoid sending people to CC level in a case where we don't immediately retrieve them back after doing so
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/industrial_lift_lowering.ogg', 100, FALSE)
animate(elevator_animation, pixel_x = ELEVATOR_DEPARTING_X, pixel_y = ELEVATOR_DEPARTING_Y, time = 4 SECONDS)
sleep(4.2 SECONDS)
animate(hatch_left, pixel_x = 0, time = 5 SECONDS)
animate(hatch_right, pixel_x = 0, time = 5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(flip_rotating_alarms)), 9 SECOND)
/// This attempts to sync up with hatch animation, by activating groups of step triggers in delayed order.
/datum/shuttle/autodock/ferry/supply/proc/handle_step_triggers()
var/obj/effect/step_trigger/cargo_elevator/trigger
var/group_key
for(var/i in 0 to 3)
group_key = "distance_[i]"
for(trigger in step_trigger_group[group_key]) // checking for the things standing on top of the hatch, then we send them to falling
for(var/atom/movable/AM in get_turf(trigger))
if(istype(AM, /obj/effect)) // avoid picking up step_triggers
continue
trigger.handle_falling(AM)
trigger.safe_to_walk = FALSE
sleep(1 SECOND)
/datum/shuttle/autodock/ferry/supply/proc/flip_rotating_alarms()
for(var/obj/machinery/rotating_alarm/RA in horizon_elevator_area)
RA.toggle_state()
/datum/shuttle/autodock/ferry/supply/proc/toggle_railings()
for(var/obj/structure/railing/retractable/R in horizon_elevator_area)
R.toggle_state()
/obj/effect/landmark/destination_helper/cargo_elevator
name = "cargo elevator destination helper"
/obj/effect/step_trigger/cargo_elevator
name = "cargo elevator shaft"
icon = 'icons/effects/map_effects.dmi'
simulated = FALSE // this prevents the effects getting transported along with the elevator
/// List of turfs within the 'area/supply/dock'. Shared between all instances.
var/static/list/possible_turfs_to_land_on = list()
/// Boolean, determines whether the movables should fall or not.
var/safe_to_walk = TRUE
/obj/effect/step_trigger/cargo_elevator/Initialize()
. = ..()
if(!possible_turfs_to_land_on.len)
possible_turfs_to_land_on = get_area_turfs(/area/supply/dock)
/obj/effect/step_trigger/cargo_elevator/Destroy()
LAZYNULL(possible_turfs_to_land_on)
return ..()
/obj/effect/step_trigger/cargo_elevator/Trigger(atom/movable/AM)
if(safe_to_walk)
return
INVOKE_ASYNC(src, PROC_REF(handle_falling), AM)
/obj/effect/step_trigger/cargo_elevator/proc/handle_falling(atom/movable/AM)
if(!isliving(AM) && !isobj(AM)) // only mobs and objects
return
if(isliving(AM))
var/mob/living/L = AM
if(L.CanAvoidGravity())
return
L.apply_effect(2, WEAKEN)
INVOKE_ASYNC(src, PROC_REF(animate_falling), AM) // this needs to run asynchronously, otherwise will cause issues
/obj/effect/step_trigger/cargo_elevator/proc/animate_falling(atom/movable/AM)
sleep(0.1 SECOND) // allowing some time for mobs to get weaken effect applied
var/oldalpha = AM.alpha
var/oldcolor = AM.color
var/old_pixel_y = AM.pixel_y
animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 1 SECOND)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.pixel_y--
sleep(0.2 SECONDS)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.visible_message(SPAN_WARNING("[AM] falls into [src]!"), SPAN_DANGER("You stumble and stare into \the [src] as you fall. It will be a long ride..."))
AM.forceMove(pick(possible_turfs_to_land_on))
AM.alpha = oldalpha
AM.color = oldcolor
AM.transform = matrix()
AM.pixel_y = old_pixel_y
if(isliving(AM))
var/mob/living/L = AM
L.adjustBruteLoss(rand(30,50))
L.dir = pick(GLOB.cardinals)
#undef SHAFT_WIDTH
#undef SHAFT_CENTER_OFFSET
#undef ELEVATOR_DEPARTING_X
#undef ELEVATOR_DEPARTING_Y
@@ -0,0 +1,8 @@
author: Kano, KingOfThePing
delete-after: True
changes:
- rscadd: "Added an elevator animation logic for cargo elevator, heavily inspired from CM."
- rscadd: "Added hatch and shaft sprites for cargo elevator, made by KingOfThePing!"
- rscadd: "Ported some sounds and sprites from CM."
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.