diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index ed4fb787b8d..1e4692b7e3b 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -269,6 +269,7 @@ CREATE TABLE `player` (
`fuid` bigint(20) NULL DEFAULT NULL,
`fupdate` smallint(4) NULL DEFAULT '0',
`afk_watch` tinyint(1) NOT NULL DEFAULT '0',
+ `parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index b0f7bbc5c82..aa5d5895e56 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -268,6 +268,7 @@ CREATE TABLE `SS13_player` (
`fuid` bigint(20) NULL DEFAULT NULL,
`fupdate` smallint(4) NULL DEFAULT '0',
`afk_watch` tinyint(1) NOT NULL DEFAULT '0',
+ `parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
diff --git a/SQL/updates/8-9.sql b/SQL/updates/8-9.sql
new file mode 100644
index 00000000000..d7f0fd19ab3
--- /dev/null
+++ b/SQL/updates/8-9.sql
@@ -0,0 +1,4 @@
+#Updating the SQL from version 8 to version 9. -affectedarc07
+#Adding new column to contain the parallax value.
+ALTER TABLE `player`
+ ADD `parallax` tinyint(1) DEFAULT '8' AFTER `fupdate`;
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 049f2541bc1..45d1d07c940 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -403,7 +403,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
-#define SQL_VERSION 8
+#define SQL_VERSION 9
// Vending machine stuff
#define CAT_NORMAL 1
@@ -485,4 +485,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..acf202f0eee 100644
--- a/code/__HELPERS/icon_smoothing.dm
+++ b/code/__HELPERS/icon_smoothing.dm
@@ -153,29 +153,33 @@
/turf/simulated/wall/diagonal_smooth(adjacencies)
adjacencies = reverse_ndir(..())
if(adjacencies)
- var/list/U = list()
+ var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, plane = FLOOR_PLANE)
+ var/list/U = list(underlay_appearance)
if(fixed_underlay)
if(fixed_underlay["space"])
- U += image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=TURF_LAYER)
+ underlay_appearance.icon = 'icons/turf/space.dmi'
+ underlay_appearance.icon_state = SPACE_ICON_STATE
+ underlay_appearance.plane = PLANE_SPACE
else
- U += image(fixed_underlay["icon"], fixed_underlay["icon_state"], layer=TURF_LAYER)
+ underlay_appearance.icon = fixed_underlay["icon"]
+ underlay_appearance.icon_state = fixed_underlay["icon_state"]
else
- var/turf/T = get_step(src, turn(adjacencies, 180))
- if(T && (T.density || T.smooth))
+ var/turned_adjacency = turn(adjacencies, 180)
+ var/turf/T = get_step(src, turned_adjacency)
+ if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
T = get_step(src, turn(adjacencies, 135))
- if(T && (T.density || T.smooth))
+ if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
T = get_step(src, turn(adjacencies, 225))
-
- if(istype(T, /turf/space) && !istype(T, /turf/space/transit))
- U += image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=TURF_LAYER)
- else if(T && !T.density && !T.smooth)
- U += T
- else if(baseturf && !initial(baseturf.density) && !initial(baseturf.smooth))
- U += image(initial(baseturf.icon), initial(baseturf.icon_state), layer=TURF_LAYER)
- else
- U += DEFAULT_UNDERLAY_IMAGE
+ //if all else fails, ask our own turf
+ if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency) && !get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
+ underlay_appearance.icon = DEFAULT_UNDERLAY_ICON
+ underlay_appearance.icon_state = DEFAULT_UNDERLAY_ICON_STATE
underlays = U
+ // Drop posters which were previously placed on this wall.
+ for(var/obj/structure/sign/poster/P in src)
+ P.roll_and_drop(src)
+
/proc/cardinal_smooth(atom/A, adjacencies)
//NW CORNER
var/nw = "1-i"
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index 4e92395f4cc..600c1105e8e 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -59,3 +59,8 @@
using = new /obj/screen/ghost/teleport()
using.screen_loc = ui_ghost_teleport
static_inventory += using
+
+/datum/hud/ghost/show_hud()
+ mymob.client.screen = list()
+ mymob.client.screen += static_inventory
+ ..()
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 0063feae9cf..e93b3b121e6 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -99,6 +99,7 @@
/datum/hud/proc/show_hud(version = 0)
if(!ismob(mymob))
return FALSE
+
if(!mymob.client)
return FALSE
@@ -172,6 +173,7 @@
mymob.update_action_buttons(1)
reorganize_alerts()
reload_fullscreen()
+ update_parallax_pref(mymob)
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..0d981962cc0
--- /dev/null
+++ b/code/_onclick/hud/parallax.dm
@@ -0,0 +1,308 @@
+/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/new_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_cached += new /obj/screen/parallax_layer/planet(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
+ if(C.prefs)
+ var/pref = C.prefs.parallax
+ if (isnull(pref))
+ pref = PARALLAX_HIGH
+ 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
+
+ C.parallax_throttle = PARALLAX_DELAY_DEFAULT
+ C.parallax_layers_max = 3
+ return TRUE
+
+/datum/hud/proc/update_parallax_pref()
+ remove_parallax()
+ create_parallax()
+ update_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, skip_windups)
+ . = 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)
+ var/datum/callback/CB = CALLBACK(src, .proc/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)
+ 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(C.new_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
+ L.update_status(mymob)
+ if(L.view_sized != C.view)
+ L.update_o(C.view)
+
+ var/change_x
+ var/change_y
+
+ if(L.absolute)
+ L.offset_x = -(posobj.x - SSparallax.planet_x_offset) * L.speed
+ L.offset_y = -(posobj.y - SSparallax.planet_y_offset) * L.speed
+ else
+ change_x = offset_x * L.speed
+ L.offset_x -= change_x
+ 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
+ var/absolute = FALSE
+ 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/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state)
+ texture_overlay.transform = matrix(1, 0, x*480, 0, 1, y*480)
+ new_overlays += texture_overlay
+
+ overlays = new_overlays
+ view_sized = view
+
+/obj/screen/parallax_layer/proc/update_status(mob/M)
+ return
+
+/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_layer/planet
+ icon_state = "planet"
+ blend_mode = BLEND_OVERLAY
+ absolute = TRUE //Status of seperation
+ speed = 3
+ layer = 30
+
+/obj/screen/parallax_layer/planet/update_status(mob/M)
+ var/turf/T = get_turf(M)
+ if(is_station_level(T.z))
+ invisibility = 0
+ else
+ invisibility = INVISIBILITY_ABSTRACT
+
+/obj/screen/parallax_layer/planet/update_o()
+ return //Shit wont move
+
+/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..039b6d2b008
--- /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..0a22c19770b 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/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm
index 6a213400d04..17807bd255b 100644
--- a/code/game/turfs/simulated/floor/chasm.dm
+++ b/code/game/turfs/simulated/floor/chasm.dm
@@ -22,6 +22,11 @@
if(!drop_stuff())
STOP_PROCESSING(SSprocessing, src)
+/turf/open/chasm/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/floors.dmi'
+ underlay_appearance.icon_state = "basalt"
+ return TRUE
+
/turf/simulated/floor/chasm/attackby(obj/item/C, mob/user, params, area/area_restriction)
..()
if(istype(C, /obj/item/stack/rods))
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index 97065cd9711..12b1654994c 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -117,6 +117,9 @@
burnt = 1
update_icon()
+/turf/open/floor/carpet/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ return FALSE
+
/turf/simulated/floor/carpet/black
icon = 'icons/turf/floors/carpet_black.dmi'
floor_tile = /obj/item/stack/tile/carpet/black
@@ -127,6 +130,7 @@
icon_state = "0"
floor_tile = /obj/item/stack/tile/fakespace
broken_states = list("damaged")
+ plane = PLANE_SPACE
/turf/simulated/floor/fakespace/New()
..()
@@ -137,3 +141,9 @@
icon_state = "arcade"
floor_tile = /obj/item/stack/tile/arcade_carpet
smooth = SMOOTH_FALSE
+
+/turf/open/floor/fakespace/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/space.dmi'
+ underlay_appearance.icon_state = SPACE_ICON_STATE
+ underlay_appearance.plane = PLANE_SPACE
+ return TRUE
diff --git a/code/game/turfs/simulated/floor/indestructible.dm b/code/game/turfs/simulated/floor/indestructible.dm
index 6c1679776e6..3705e24eea0 100644
--- a/code/game/turfs/simulated/floor/indestructible.dm
+++ b/code/game/turfs/simulated/floor/indestructible.dm
@@ -81,5 +81,8 @@
planetary_atmos = TRUE
desc = "A floor with a square pattern. It's faintly cool to the touch."
+/turf/open/indestructible/hierophant/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ return FALSE
+
/turf/simulated/floor/indestructible/hierophant/two
icon_state = "hierophant2"
diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm
index 0882bb18766..66d7eaca83d 100644
--- a/code/game/turfs/simulated/floor/lava.dm
+++ b/code/game/turfs/simulated/floor/lava.dm
@@ -41,6 +41,11 @@
/turf/simulated/floor/plating/lava/remove_plating()
return
+/turf/open/lava/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/floors.dmi'
+ underlay_appearance.icon_state = "basalt"
+ return TRUE
+
/turf/simulated/floor/plating/lava/proc/is_safe()
var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile))
var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache)
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index 119f0b000d9..aee23831050 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -48,6 +48,13 @@
setDir(angle2dir(rotation + dir2angle(dir)))
queue_smooth(src)
+/turf/simulated/mineral/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ if(turf_type)
+ underlay_appearance.icon = initial(turf_type.icon)
+ underlay_appearance.icon_state = initial(turf_type.icon_state)
+ return TRUE
+ return ..()
+
/turf/simulated/mineral/attackby(var/obj/item/pickaxe/P as obj, mob/user as mob, params)
if(!user.IsAdvancedToolUser())
to_chat(usr, "You don't have the dexterity to do this!")
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 0d6428b10a2..daecd225355 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))
@@ -105,6 +106,12 @@
..()
if((!(A) || !(src in A.locs)))
return
+
+ if(ismob(A))
+ var/mob/M = A
+ if(M && M.client && M.client.new_parallax_movedir)
+ M.client.new_parallax_movedir = 0
+ M.update_parallax_contents()
if(destination_z && destination_x && destination_y)
A.forceMove(locate(destination_x, destination_y, destination_z))
@@ -262,4 +269,10 @@
/turf/space/attack_ghost(mob/dead/observer/user)
if(destination_z)
var/turf/T = locate(destination_x, destination_y, destination_z)
- user.forceMove(T)
\ No newline at end of file
+ user.forceMove(T)
+
+/turf/space/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/space.dmi'
+ underlay_appearance.icon_state = SPACE_ICON_STATE
+ underlay_appearance.plane = PLANE_SPACE
+ return TRUE
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index 79bf171ce1f..15c553b3f95 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 = PLANE_SPACE
//Overwrite because we dont want people building rods in space.
/turf/space/transit/attackby(obj/O as obj, mob/user as mob, params)
@@ -151,3 +152,9 @@
icon_state = "speedspace_ns_[state]"
transform = turn(matrix(), angle)
+
+/turf/space/transit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/space.dmi'
+ underlay_appearance.icon_state = SPACE_ICON_STATE
+ underlay_appearance.plane = PLANE_SPACE
+ return TRUE
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index e315ad26ad0..51285932f00 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -470,6 +470,12 @@
if(ismob(A) || .)
A.ratvar_act()
+/turf/proc/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = icon
+ underlay_appearance.icon_state = icon_state
+ underlay_appearance.dir = adjacency_dir
+ return TRUE
+
/turf/proc/add_blueprints(atom/movable/AM)
var/image/I = new
I.appearance = AM.appearance
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 95f6c16ba97..2d4092ff159 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)
+ 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..7fce23b9e4c 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_HIGH,
+ "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/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index cdbfe2afb2f..6090538f2b3 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -20,7 +20,8 @@
clientfps,
atklog,
fuid,
- afk_watch
+ afk_watch,
+ parallax
FROM [format_table_name("player")]
WHERE ckey='[C.ckey]'"}
)
@@ -54,6 +55,7 @@
atklog = text2num(query.item[18])
fuid = text2num(query.item[19])
afk_watch = text2num(query.item[20])
+ parallax = text2num(query.item[21])
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
@@ -75,6 +77,7 @@
atklog = sanitize_integer(atklog, 0, 100, initial(atklog))
fuid = sanitize_integer(fuid, 0, 10000000, initial(fuid))
afk_watch = sanitize_integer(afk_watch, 0, 1, initial(afk_watch))
+ parallax = sanitize_integer(parallax, 0, 16, initial(parallax))
return 1
/datum/preferences/proc/save_preferences(client/C)
@@ -105,7 +108,8 @@
ghost_anonsay='[ghost_anonsay]',
clientfps='[clientfps]',
atklog='[atklog]',
- afk_watch='[afk_watch]'
+ afk_watch='[afk_watch]',
+ parallax='[parallax]'
WHERE ckey='[C.ckey]'"}
)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 731f53c7838..ab855b5dc80 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -236,6 +236,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return 1
/mob/dead/observer/Move(NewLoc, direct)
+ update_parallax_contents()
following = null
setDir(direct)
ghostimage.setDir(dir)
@@ -417,6 +418,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 +513,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/on_move.dm b/code/modules/shuttle/on_move.dm
index 67765ef8ac5..77c6a21dd0d 100644
--- a/code/modules/shuttle/on_move.dm
+++ b/code/modules/shuttle/on_move.dm
@@ -30,17 +30,22 @@
if(id_tag == "s_docking_airlock")
INVOKE_ASYNC(src, .proc/lock)
-/mob/onShuttleMove()
+/mob/onShuttleMove(turf/oldT, turf/T1, rotation, travelDir)
if(!move_on_shuttle)
return 0
. = ..()
if(!.)
return
- if(client)
- if(buckled)
- shake_camera(src, 2, 1) // turn it down a bit come on
- else
- shake_camera(src, 7, 1)
+ if(!client)
+ return
+
+ if(buckled)
+ shake_camera(src, 2, 1) // turn it down a bit come on
+ else
+ shake_camera(src, 7, 1)
+
+ client.new_parallax_movedir = travelDir ? WEST : NORTH
+ update_parallax_contents()
/mob/living/carbon/onShuttleMove()
. = ..()
@@ -54,8 +59,16 @@
if(smooth)
queue_smooth(src)
+/mob/postDock(obj/docking_port/S1, transit, list/parallax_mobs)
+ if(!client)
+ return
+ if(transit)
+ parallax_mobs.Add(src)
+ return
+ client.new_parallax_movedir = 0
+ update_parallax_contents()
+
/obj/machinery/door/airlock/postDock(obj/docking_port/stationary/S1)
. = ..()
if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock")
INVOKE_ASYNC(src, .proc/unlock)
-
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 51ba6c6bad1..b2d710ba215 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()
@@ -215,6 +215,7 @@
var/roundstart_move //id of port to send shuttle to at roundstart
var/travelDir = 0 //direction the shuttle would travel in
var/rebuildable = 0 //can build new shuttle consoles for this one
+ var/list/parallax_mobs = list() //mobs to unparallax
var/obj/docking_port/stationary/destination
var/obj/docking_port/stationary/previous
@@ -358,7 +359,7 @@
var/obj/docking_port/stationary/S0 = get_docked()
var/obj/docking_port/stationary/S1 = findTransitDock()
if(S1)
- if(dock(S1))
+ if(dock(S1, , TRUE))
WARNING("shuttle \"[id]\" could not enter transit space. Docked at [S0 ? S0.id : "null"]. Transit dock [S1 ? S1.id : "null"].")
else
previous = S0
@@ -429,7 +430,7 @@
//this is the main proc. It instantly moves our mobile port to stationary port S1
//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc
-/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1, force=FALSE)
+/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1, force=FALSE, transit=FALSE)
// Crashing this ship with NO SURVIVORS
if(S1.get_docked() == src)
remove_ripples()
@@ -494,7 +495,7 @@
//move mobile to new location
for(var/atom/movable/AM in T0)
- AM.onShuttleMove(T0, T1, rotation)
+ AM.onShuttleMove(T0, T1, rotation, travelDir)
if(rotation)
T1.shuttleRotate(rotation)
@@ -516,7 +517,15 @@
var/turf/T1 = A1
T1.postDock(S1)
for(var/atom/movable/M in T1)
- M.postDock(S1)
+ M.postDock(S1, transit, parallax_mobs)
+
+ // For mobs who move away from a transiting shuttle for whatever reason (teleportation, jumping, etc) so that they don't get stuck parallaxing
+ if(!transit)
+ for(var/mob/M in parallax_mobs)
+ if(M.client && M.client.new_parallax_movedir)
+ M.client.new_parallax_movedir = 0
+ M.update_parallax_contents()
+ parallax_mobs = list()
loc = S1.loc
dir = S1.dir
diff --git a/config/example/dbconfig.txt b/config/example/dbconfig.txt
index 2404119d52f..ad859d92543 100644
--- a/config/example/dbconfig.txt
+++ b/config/example/dbconfig.txt
@@ -9,7 +9,7 @@
## This value must be set to the version of the paradise schema in use.
## If this value does not match, the SQL database will not be loaded and an error will be generated.
## Roundstart will be delayed.
-DB_VERSION 8
+DB_VERSION 9
## Server the MySQL database can be found at.
# Examples: localhost, 200.135.5.43, www.mysqldb.com, etc.
diff --git a/icons/effects/parallax.dmi b/icons/effects/parallax.dmi
new file mode 100644
index 00000000000..22f091ff11a
Binary files /dev/null and b/icons/effects/parallax.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 86eb2825b04..bd4af9dc9fe 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"
@@ -224,6 +225,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"