diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 723fe6b0762..273159fe948 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -402,4 +402,10 @@ #define DICE_NOT_RIGGED 1 #define DICE_BASICALLY_RIGGED 2 -#define DICE_TOTALLY_RIGGED 3 \ No newline at end of file +#define DICE_TOTALLY_RIGGED 3 + +// Parallax +#define PARALLAX_DELAY_DEFAULT world.tick_lag +#define PARALLAX_DELAY_MED 1 +#define PARALLAX_DELAY_LOW 2 +#define PARALLAX_LOOP_TIME 25 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 9582fddccfe..a28d68a392b 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -63,3 +63,12 @@ #define EXP_TYPE_WHITELIST "Whitelist" #define EXP_DEPT_TYPE_LIST list(EXP_TYPE_SERVICE, EXP_TYPE_MEDICAL, EXP_TYPE_ENGINEERING, EXP_TYPE_SCIENCE, EXP_TYPE_SECURITY, EXP_TYPE_COMMAND, EXP_TYPE_SILICON, EXP_TYPE_SPECIAL) + +// Defines just for parallax because its levels make storing it in the regular prefs a pain in the ass +// These dont need to be bitflags because there isnt going to be more than one at a time of these active +// But its gonna piss off my OCD if it isnt bitflags, so deal with it, -affected +#define PARALLAX_DISABLE 1 +#define PARALLAX_LOW 2 +#define PARALLAX_MED 4 +#define PARALLAX_HIGH 8 +#define PARALLAX_INSANE 16 diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 11c86940b75..9156ebf8717 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -156,9 +156,13 @@ var/list/U = list() if(fixed_underlay) if(fixed_underlay["space"]) - U += image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=TURF_LAYER) + var/image/I = image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=TURF_LAYER) + I.plane = PLANE_SPACE + U += I else - U += image(fixed_underlay["icon"], fixed_underlay["icon_state"], layer=TURF_LAYER) + var/image/I = image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=TURF_LAYER) + I.plane = PLANE_SPACE + U += I else var/turf/T = get_step(src, turn(adjacencies, 180)) if(T && (T.density || T.smooth)) diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index 4e92395f4cc..8207fc746e3 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -59,3 +59,9 @@ using = new /obj/screen/ghost/teleport() using.screen_loc = ui_ghost_teleport static_inventory += using + +/datum/hud/ghost/show_hud() + mymob.client.screen = list() + create_parallax() + mymob.client.screen += static_inventory + ..() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 0063feae9cf..c4fbd0da7ac 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -172,6 +172,7 @@ mymob.update_action_buttons(1) reorganize_alerts() reload_fullscreen() + create_parallax() plane_masters_update() /datum/hud/proc/plane_masters_update() diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm new file mode 100644 index 00000000000..32b8cf534f2 --- /dev/null +++ b/code/_onclick/hud/parallax.dm @@ -0,0 +1,268 @@ +/client + var/list/parallax_layers + var/list/parallax_layers_cached + var/static/list/parallax_static_layers_tail = newlist(/obj/screen/parallax_pmaster, /obj/screen/parallax_space_whitifier) + var/atom/movable/movingmob + var/turf/previous_turf + var/dont_animate_parallax //world.time of when we can state animate()ing parallax again + var/last_parallax_shift //world.time of last update + var/parallax_throttle = 0 //ds between updates + var/parallax_movedir = 0 + var/parallax_layers_max = 3 + var/parallax_animate_timer + +/datum/hud/proc/create_parallax() + var/client/C = mymob.client + if (!apply_parallax_pref()) + return + + if(!length(C.parallax_layers_cached)) + C.parallax_layers_cached = list() + C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_1(null, C.view) + C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_2(null, C.view) + + C.parallax_layers = C.parallax_layers_cached.Copy() + + if (length(C.parallax_layers) > C.parallax_layers_max) + C.parallax_layers.len = C.parallax_layers_max + + C.screen |= (C.parallax_layers + C.parallax_static_layers_tail) + +/datum/hud/proc/remove_parallax() + var/client/C = mymob.client + C.screen -= (C.parallax_layers_cached + C.parallax_static_layers_tail) + C.parallax_layers = null + +/datum/hud/proc/apply_parallax_pref() + var/client/C = mymob.client + switch(C.prefs.parallax) + if (PARALLAX_INSANE) + C.parallax_throttle = FALSE + C.parallax_layers_max = 4 + return TRUE + + if (PARALLAX_MED) + C.parallax_throttle = PARALLAX_DELAY_MED + C.parallax_layers_max = 2 + return TRUE + + if (PARALLAX_LOW) + C.parallax_throttle = PARALLAX_DELAY_LOW + C.parallax_layers_max = 1 + return TRUE + + if (PARALLAX_DISABLE) + return FALSE + + else + C.parallax_throttle = PARALLAX_DELAY_DEFAULT + C.parallax_layers_max = 3 + return TRUE + +/datum/hud/proc/update_parallax_pref() + remove_parallax() + create_parallax() + +// This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation) +// Well, it would if our shuttle code had dynamic areas +/datum/hud/proc/set_parallax_movedir(new_parallax_movedir) + . = FALSE + var/client/C = mymob.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/obj/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) + if(NORTH) + newtransform = matrix(1, 0, 0, 0, 1, 480) + if(SOUTH) + newtransform = matrix(1, 0, 0, 0, 1,-480) + if(EAST) + newtransform = matrix(1, 0, 480, 0, 1, 0) + if(WEST) + newtransform = matrix(1, 0,-480, 0, 1, 0) + + var/shortesttimer + for(var/thing in C.parallax_layers) + var/obj/screen/parallax_layer/L = thing + + 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) + C.parallax_animate_timer = addtimer(src, .update_parallax_motionblur, min(shortesttimer, PARALLAX_LOOP_TIME), TIMER_CLIENT_TIME, C, animatedir, new_parallax_movedir, newtransform) + +/datum/hud/proc/update_parallax_motionblur(client/C, animatedir, new_parallax_movedir, matrix/newtransform) + C.parallax_animate_timer = FALSE + for(var/thing in C.parallax_layers) + var/obj/screen/parallax_layer/L = thing + if (!new_parallax_movedir) + animate(L) + continue + + var/newstate = initial(L.icon_state) + if (animatedir) + if(animatedir == NORTH || animatedir == SOUTH) + newstate += "_vertical" + else + newstate += "_horizontal" + + var/T = PARALLAX_LOOP_TIME / L.speed + + if (newstate in icon_states(L.icon)) + L.icon_state = newstate + L.update_o(C.view) + + L.transform = newtransform + + animate(L, transform = matrix(), time = T, loop = -1, flags = ANIMATION_END_NOW) + +/datum/hud/proc/update_parallax() + var/client/C = mymob.client + var/turf/posobj = get_turf(C.eye) + var/area/areaobj = posobj.loc + + // Update the movement direction of the parallax if necessary (for shuttles) + set_parallax_movedir(areaobj.parallax_movedir) + + var/force + if(!C.previous_turf || (C.previous_turf.z != posobj.z)) + C.previous_turf = posobj + force = TRUE + + if (!force && world.time < C.last_parallax_shift+C.parallax_throttle) + return + + //Doing it this way prevents parallax layers from "jumping" when you change Z-Levels. + var/offset_x = posobj.x - C.previous_turf.x + var/offset_y = posobj.y - C.previous_turf.y + + if(!offset_x && !offset_y && !force) + return + + var/last_delay = world.time - C.last_parallax_shift + last_delay = min(last_delay, C.parallax_throttle) + C.previous_turf = posobj + C.last_parallax_shift = world.time + + for(var/thing in C.parallax_layers) + var/obj/screen/parallax_layer/L = thing + if (L.view_sized != C.view) + L.update_o(C.view) + var/change_x = offset_x * L.speed + L.offset_x -= change_x + var/change_y = offset_y * L.speed + L.offset_y -= change_y + if(L.offset_x > 240) + L.offset_x -= 480 + if(L.offset_x < -240) + L.offset_x += 480 + if(L.offset_y > 240) + L.offset_y -= 480 + if(L.offset_y < -240) + L.offset_y += 480 + + + if(!areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && (offset_x || offset_y) && abs(offset_x) <= max(C.parallax_throttle/world.tick_lag+1,1) && abs(offset_y) <= max(C.parallax_throttle/world.tick_lag+1,1) && (round(abs(change_x)) > 1 || round(abs(change_y)) > 1)) + L.transform = matrix(1, 0, offset_x*L.speed, 0, 1, offset_y*L.speed) + animate(L, transform=matrix(), time = last_delay) + + L.screen_loc = "CENTER-7:[round(L.offset_x,1)],CENTER-7:[round(L.offset_y,1)]" + +/atom/movable/proc/update_parallax_contents() + if(length(client_mobs_in_contents)) + for(var/thing in client_mobs_in_contents) + var/mob/M = thing + if(M && M.client && M.hud_used && length(M.client.parallax_layers)) + M.hud_used.update_parallax() + +/obj/screen/parallax_layer + icon = 'icons/effects/parallax.dmi' + var/speed = 1 + var/offset_x = 0 + var/offset_y = 0 + var/view_sized + blend_mode = BLEND_ADD + plane = PLANE_SPACE_PARALLAX + screen_loc = "CENTER-7,CENTER-7" + mouse_opacity = 0 + + +/obj/screen/parallax_layer/New(view) + ..() + if (!view) + view = world.view + update_o(view) + +/obj/screen/parallax_layer/proc/update_o(view) + if (!view) + view = world.view + var/list/new_overlays = list() + var/count = Ceiling(view/(480/world.icon_size))+1 + for(var/x in -count to count) + for(var/y in -count to count) + if(x == 0 && y == 0) + continue + var/image/I = image(icon, null, icon_state) + I.transform = matrix(1, 0, x*480, 0, 1, y*480) + new_overlays += I + + overlays = new_overlays + view_sized = view + +/obj/screen/parallax_layer/layer_1 + icon_state = "layer1" + speed = 0.6 + layer = 1 + +/obj/screen/parallax_layer/layer_2 + icon_state = "layer2" + speed = 1 + layer = 2 + +/obj/screen/parallax_pmaster + appearance_flags = PLANE_MASTER + plane = PLANE_SPACE_PARALLAX + blend_mode = BLEND_MULTIPLY + mouse_opacity = FALSE + screen_loc = "CENTER-7,CENTER-7" + +/obj/screen/parallax_space_whitifier + appearance_flags = PLANE_MASTER + plane = PLANE_SPACE + color = list( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 1, 1, 1, 1, + 0, 0, 0, 0 + ) + screen_loc = "CENTER-7,CENTER-7" + + +#undef LOOP_NONE +#undef LOOP_NORMAL +#undef LOOP_REVERSE +#undef LOOP_TIME \ No newline at end of file diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 797384b8336..a988277012b 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -17,6 +17,7 @@ else following = null forceMove(get_turf(A)) + update_parallax_contents() /mob/dead/observer/ClickOn(var/atom/A, var/params) if(client.click_intercept) diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm new file mode 100644 index 00000000000..64299fda383 --- /dev/null +++ b/code/controllers/subsystem/parallax.dm @@ -0,0 +1,44 @@ +SUBSYSTEM_DEF(parallax) + name = "Parallax" + wait = 2 + flags = SS_POST_FIRE_TIMING | SS_BACKGROUND + priority = FIRE_PRIORITY_PARALLAX + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + var/list/currentrun + var/planet_x_offset = 128 + var/planet_y_offset = 128 + +/datum/controller/subsystem/parallax/Initialize(timeofday) + . = ..() + planet_y_offset = rand(100, 160) + planet_x_offset = rand(100, 160) + +/datum/controller/subsystem/parallax/fire(resumed = 0) + if (!resumed) + src.currentrun = GLOB.clients.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(length(currentrun)) + var/client/C = currentrun[currentrun.len] + currentrun.len-- + if (!C || !C.eye) + if (MC_TICK_CHECK) + return + continue + var/atom/movable/A = C.eye + if(!istype(A)) + continue + for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc); + + if(A != C.movingmob) + if(C.movingmob != null) + C.movingmob.client_mobs_in_contents -= C.mob + UNSETEMPTY(C.movingmob.client_mobs_in_contents) + LAZYINITLIST(A.client_mobs_in_contents) + A.client_mobs_in_contents += C.mob + C.movingmob = A + if (MC_TICK_CHECK) + return + currentrun = null diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 2e7448ec5c8..442bde3aa68 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -67,6 +67,8 @@ var/can_get_auto_cryod = TRUE var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. + var/parallax_movedir = 0 + /area/Initialize(mapload) GLOB.all_areas += src icon_state = "" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 1a23dc71070..d726a810894 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -26,7 +26,7 @@ var/inertia_move_delay = 5 var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move - + var/list/client_mobs_in_contents var/area/areaMaster /atom/movable/New() @@ -215,6 +215,8 @@ if(!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(Dir) + if (length(client_mobs_in_contents)) + update_parallax_contents() return TRUE // Previously known as HasEntered() diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index 97065cd9711..fcf4c7c15c5 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -127,6 +127,7 @@ icon_state = "0" floor_tile = /obj/item/stack/tile/fakespace broken_states = list("damaged") + plane = PLANE_SPACE /turf/simulated/floor/fakespace/New() ..() diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 0d6428b10a2..ff9045a0436 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -16,6 +16,7 @@ var/destination_z var/destination_x var/destination_y + plane = PLANE_SPACE /turf/space/Initialize(mapload) if(!istype(src, /turf/space/transit)) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 79bf171ce1f..4f621f80ddc 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,5 +1,6 @@ /turf/space/transit var/pushdirection // push things that get caught in the transit tile this direction + plane = TURF_LAYER //Overwrite because we dont want people building rods in space. /turf/space/transit/attackby(obj/O as obj, mob/user as mob, params) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 95f6c16ba97..4ffaf0c0a6d 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -433,6 +433,9 @@ GLOB.admins -= src GLOB.directory -= ckey GLOB.clients -= src + if(movingmob != null) + movingmob.client_mobs_in_contents -= mob + UNSETEMPTY(movingmob.client_mobs_in_contents) Master.UpdateTickRate() return ..() diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 97f9ddc6169..1d54e8b0116 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -201,6 +201,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts //Gear stuff var/list/gear = list() var/gear_tab = "General" + // Parallax + var/parallax = PARALLAX_HIGH /datum/preferences/New(client/C) parent = C @@ -462,6 +464,19 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "OOC Color:     Change
" if(config.allow_Metadata) dat += "OOC Notes: Edit
" + dat += "Parallax (Fancy Space): " + switch (parallax) + if (PARALLAX_LOW) + dat += "Low" + if (PARALLAX_MED) + dat += "Medium" + if (PARALLAX_INSANE) + dat += "Insane" + if (PARALLAX_DISABLE) + dat += "Disabled" + else + dat += "High" + dat += "
" dat += "Play Admin MIDIs: [(sound & SOUND_MIDI) ? "Yes" : "No"]
" dat += "Play Lobby Music: [(sound & SOUND_LOBBY) ? "Yes" : "No"]
" dat += "Randomized Character Slot: [randomslot ? "Yes" : "No"]
" @@ -2072,6 +2087,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(href_list["tab"]) current_tab = text2num(href_list["tab"]) + if("ambientocclusion") toggles ^= AMBIENT_OCCLUSION if(parent && parent.screen && parent.screen.len) @@ -2080,6 +2096,19 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(toggles & AMBIENT_OCCLUSION) PM.filters += FILTER_AMBIENT_OCCLUSION + if("parallax") + var/parallax_styles = list( + "Off" = PARALLAX_DISABLE, + "Low" = PARALLAX_LOW, + "Medium" = PARALLAX_MED, + "High" = PARALLAX_INSANE, + "Insane" = PARALLAX_INSANE + ) + parallax = parallax_styles[input(user, "Pick a parallax style", "Parallax Style") as null|anything in parallax_styles] + if (parent && parent.mob && parent.mob.hud_used) + parent.mob.hud_used.update_parallax_pref() + + ShowChoices(user) return 1 diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 731f53c7838..d95f6c6431e 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -417,6 +417,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return forceMove(pick(L)) + update_parallax_contents() following = null /mob/dead/observer/verb/follow() @@ -511,6 +512,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. A.forceMove(T) + M.update_parallax_contents() + following = null return to_chat(A, "This mob is not located in the game world.") diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 7344c6469c4..c3c47235b91 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -31,6 +31,7 @@ ai.camera_visibility(src) if(ai.client && !ai.multicam_on) ai.client.eye = src + update_parallax_contents() //Holopad if(ai.master_multicam) ai.master_multicam.refresh_view() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 51ba6c6bad1..2d3b03b967f 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -189,7 +189,7 @@ /obj/docking_port/stationary/transit name = "In transit" turf_type = /turf/space/transit - + var/area/shuttle/transit/assigned_area lock_shuttle_doors = 1 /obj/docking_port/stationary/transit/register() diff --git a/icons/effects/parallax.dmi b/icons/effects/parallax.dmi new file mode 100644 index 00000000000..181b76007d4 Binary files /dev/null and b/icons/effects/parallax.dmi differ diff --git a/paradise.dme b/paradise.dme index afaa97e1919..541b6ccf1df 100644 --- a/paradise.dme +++ b/paradise.dme @@ -143,6 +143,7 @@ #include "code\_onclick\hud\monkey.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\other_mobs.dm" +#include "code\_onclick\hud\parallax.dm" #include "code\_onclick\hud\picture_in_picture.dm" #include "code\_onclick\hud\plane_master.dm" #include "code\_onclick\hud\radial.dm" @@ -223,6 +224,7 @@ #include "code\controllers\subsystem\npcpool.dm" #include "code\controllers\subsystem\overlays.dm" #include "code\controllers\subsystem\radio.dm" +#include "code\controllers\subsystem\parallax.dm" #include "code\controllers\subsystem\shuttles.dm" #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\sun.dm"