diff --git a/code/game/objects/effects/spawners/structure.dm b/code/game/objects/effects/spawners/structure.dm
index 8048e9d96d9..7f462ff2480 100644
--- a/code/game/objects/effects/spawners/structure.dm
+++ b/code/game/objects/effects/spawners/structure.dm
@@ -375,3 +375,18 @@ again.
icon = 'icons/obj/structures_spawners.dmi'
icon_state = "electrified_grille"
spawn_list = list(/obj/structure/grille, /obj/structure/cable)
+
+///flipped tables
+/obj/effect/spawner/structure/flipped_table
+ name = "flipped table spawner"
+ icon = 'icons/obj/flipped_tables.dmi'
+ icon_state = "table"
+ ///just change this whatever table type you want, has to be a table subtype though.
+ var/table_to_spawn = /obj/structure/table
+
+/obj/effect/spawner/structure/flipped_table/Initialize(mapload)
+ . = ..()
+ var/obj/structure/table/table_to_flipped = new table_to_spawn(loc)
+ table_to_flipped.dir = dir
+ table_to_flipped.flip_table()
+
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index b57c527e075..b438e42c49f 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -29,6 +29,9 @@
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_TABLES
canSmoothWith = SMOOTH_GROUP_TABLES
+ var/static/list/turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY, TRAIT_IMMERSE_STOPPED)
+ ///a bit fucky, I know. but this is needed to get sorted on init smoothing groups stored
+ var/list/on_init_smoothed_vars
var/frame = /obj/structure/table_frame
var/framestack = /obj/item/stack/rods
var/glass_shard_type = /obj/item/shard
@@ -37,6 +40,16 @@
var/buildstackamount = 1
var/framestackamount = 2
var/deconstruction_ready = TRUE
+ ///Whether or not the table could be flipped or not
+ var/can_flip = TRUE
+ ///Whether or not the table is flipped
+ var/is_flipped = FALSE
+ /// Whether or not when flipped, it ignores PASS_GLASS flag
+ var/is_transparent = FALSE
+ /// If you don't have sprites for flipped tables, you can use matrices instead. looks ever-slightly worse.
+ var/use_matrices_instead = FALSE
+ /// Matrix to return to on unflipping table
+ var/matrix/before_flipped_matrix
/obj/structure/table/Initialize(mapload, obj/structure/table_frame/frame_used, obj/item/stack/stack_used)
. = ..()
@@ -45,21 +58,33 @@
if(stack_used)
apply_stack_properties(stack_used)
- AddElement(/datum/element/footstep_override, priority = STEP_SOUND_TABLE_PRIORITY)
-
- make_climbable()
+ before_flipped_matrix = transform
+ on_init_smoothed_vars = list(smoothing_groups, canSmoothWith)
var/static/list/loc_connections = list(
COMSIG_LIVING_DISARM_COLLIDE = PROC_REF(table_living),
+ COMSIG_ATOM_EXIT = PROC_REF(on_exit),
)
AddElement(/datum/element/connect_loc, loc_connections)
- var/static/list/give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY, TRAIT_IMMERSE_STOPPED)
- AddElement(/datum/element/give_turf_traits, give_turf_traits)
register_context()
+ if(can_flip)
+ AddElement( \
+ /datum/element/contextual_screentip_bare_hands, \
+ rmb_text = "Flip", \
+ )
+
ADD_TRAIT(src, TRAIT_COMBAT_MODE_SKIP_INTERACTION, INNATE_TRAIT)
+ if(can_flip && is_flipped)
+ flip_table()
+ return
+
+ make_climbable()
+ AddElement(/datum/element/give_turf_traits, turf_traits)
+ AddElement(/datum/element/footstep_override, priority = STEP_SOUND_TABLE_PRIORITY)
+
/// Applies additional properties based on the frame used to construct this table.
/obj/structure/table/proc/apply_frame_properties(obj/structure/table_frame/frame_used)
frame = frame_used.type
@@ -75,6 +100,76 @@
AddElement(/datum/element/climbable)
AddElement(/datum/element/elevation, pixel_shift = 12)
+//proc that adds elements present in normal tables
+/obj/structure/table/proc/unflip_table()
+ playsound(src, 'sound/items/trayhit/trayhit2.ogg', 100)
+ make_climbable()
+ AddElement(/datum/element/give_turf_traits, turf_traits)
+ AddElement(/datum/element/footstep_override, priority = STEP_SOUND_TABLE_PRIORITY)
+ //resets vars from table being flipped
+ layer = TABLE_LAYER
+ smoothing_flags |= SMOOTH_BITMASK
+ pass_flags_self |= PASSTABLE
+ if(use_matrices_instead)
+ animate(src, transform = before_flipped_matrix, time = 0)
+ else
+ icon = initial(icon)
+ icon_state = initial(icon_state)
+ smoothing_groups = on_init_smoothed_vars[1]
+ canSmoothWith = on_init_smoothed_vars[2]
+ update_appearance()
+ is_flipped = FALSE
+
+//proc that removes elements present in now-flipped tables
+/obj/structure/table/proc/flip_table(new_dir)
+ playsound(src, 'sound/items/trayhit/trayhit1.ogg', 100)
+ RemoveElement(/datum/element/climbable)
+ RemoveElement(/datum/element/footstep_override, priority = STEP_SOUND_TABLE_PRIORITY)
+ RemoveElement(/datum/element/give_turf_traits, turf_traits)
+ RemoveElement(/datum/element/elevation, pixel_shift = 12)
+
+ //change icons
+ layer = LOW_ITEM_LAYER
+ if(new_dir & EAST) // Dirs need to be part of the 4 main cardinal directions so proc/CanAllowThrough isn't fucky wucky
+ new_dir = EAST
+ else if(new_dir & WEST)
+ new_dir = WEST
+ dir = new_dir
+ if(new_dir == SOUTH)
+ layer = ABOVE_MOB_LAYER
+
+ var/turf/throw_target = get_step(src, src.dir)
+ if(!isnull(throw_target))
+ for(var/atom/movable/movable_entity in src.loc)
+ if(is_able_to_throw(src, movable_entity))
+ movable_entity.safe_throw_at(throw_target, range = 1, speed = 1, force = MOVE_FORCE_NORMAL, gentle = TRUE)
+
+ smoothing_flags &= ~SMOOTH_BITMASK
+ smoothing_groups = null
+ canSmoothWith = null
+ pass_flags_self &= ~PASSTABLE
+
+ if(use_matrices_instead)
+ icon_state = initial(icon_state)
+ before_flipped_matrix = transform
+ var/matrix/transform_matrix = matrix(1, 0, 0, 0, 0.350, 9) // "flips" the table
+ //there's probably a nicer way to do this but whatever. rotates the table according to the dir
+ if(dir == EAST)
+ transform_matrix.Turn(90)
+ if(dir == SOUTH)
+ transform_matrix.Turn(180)
+ if(dir == WEST)
+ transform_matrix.Turn(270)
+ animate(src, transform = transform_matrix, time = 0)
+ else
+ icon = 'icons/obj/flipped_tables.dmi'
+ icon_state = base_icon_state
+
+ update_appearance()
+ QUEUE_SMOOTH_NEIGHBORS(src)
+
+ is_flipped = TRUE
+
/obj/structure/table/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
@@ -100,11 +195,55 @@
/obj/structure/table/examine(mob/user)
. = ..()
+ if(is_flipped)
+ . += span_notice("It's been flipped on its side!")
. += deconstruction_hints(user)
/obj/structure/table/proc/deconstruction_hints(mob/user)
return span_notice("The top is screwed on, but the main bolts are also visible.")
+/obj/structure/table/proc/on_exit(datum/source, atom/movable/leaving, direction)
+ SIGNAL_HANDLER
+ if(!is_flipped)
+ return
+
+ if(leaving.movement_type & PHASING)
+ return
+
+ if(leaving == src)
+ return
+ if(is_transparent) //Glass table, jolly ranchers pass
+ if(istype(leaving) && (leaving.pass_flags & PASSGLASS))
+ return
+
+ if(istype(leaving, /obj/projectile))
+ return
+
+ if(direction == dir)
+ leaving.Bump(src)
+ return COMPONENT_ATOM_BLOCK_EXIT
+
+/obj/structure/table/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(.)
+ return
+
+ if(!is_flipped)
+ return FALSE
+
+ if(is_transparent) //Glass table, jolly ranchers pass
+ if(istype(mover) && (mover.pass_flags & PASSGLASS))
+ return TRUE
+ if(isprojectile(mover))
+ var/obj/projectile/proj = mover
+ //Lets through bullets shot from behind the cover of the table
+ if(proj.movement_vector && angle2dir_cardinal(proj.movement_vector.angle) == dir)
+ return TRUE
+ return FALSE
+ if(border_dir == dir)
+ return FALSE
+ return TRUE
+
/obj/structure/table/update_icon(updates=ALL)
. = ..()
if((updates & UPDATE_SMOOTHING) && (smoothing_flags & USES_SMOOTHING))
@@ -120,6 +259,8 @@
return attack_hand(user, modifiers)
/obj/structure/table/attack_hand(mob/living/user, list/modifiers)
+ if(is_flipped)
+ return
if(Adjacent(user) && user.pulling)
if(isliving(user.pulling))
var/mob/living/pushed_mob = user.pulling
@@ -154,6 +295,41 @@
user.stop_pulling()
return ..()
+/obj/structure/table/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(!istype(user) || !user.can_interact_with(src))
+ return FALSE
+
+ if(!can_flip)
+ return
+
+ if(!is_flipped)
+ user.balloon_alert_to_viewers("flipping table...")
+ if(!do_after(user, max_integrity * 0.25))
+ return
+
+ flip_table(get_dir(user, src))
+
+ return
+
+ else
+ user.balloon_alert_to_viewers("flipping table upright...")
+ if(do_after(user, max_integrity * 0.25, src))
+ unflip_table()
+ return TRUE
+
+/obj/structure/table/proc/is_able_to_throw(obj/structure/table, atom/movable/movable_entity)
+ if (movable_entity == table) //Thing is not the table
+ return FALSE
+ if (movable_entity.anchored) //Thing isn't anchored
+ return FALSE
+ if(!isliving(movable_entity) && !isobj(movable_entity)) //Thing isn't an obj or mob
+ return FALSE
+ if(movable_entity.throwing || (movable_entity.movement_type & (FLOATING|FLYING)) || HAS_TRAIT(movable_entity, TRAIT_IGNORE_ELEVATION)) //Thing isn't flying/floating
+ return FALSE
+
+ return TRUE
+
/obj/structure/table/attack_tk(mob/user)
return
@@ -163,8 +339,9 @@
return
if(mover.throwing)
return TRUE
- if(locate(/obj/structure/table) in get_turf(mover))
- return TRUE
+ for(var/obj/structure/table/table in get_turf(mover))
+ if(!table.is_flipped)
+ return TRUE
/obj/structure/table/CanAStarPass(to_dir, datum/can_pass_info/pass_info)
if(!density)
@@ -243,6 +420,9 @@
if(.)
return .
+ if(is_flipped)
+ return .
+
if(istype(tool, /obj/item/toy/cards/deck))
. = deck_act(user, tool, modifiers, !!LAZYACCESS(modifiers, RIGHT_CLICK))
if(istype(tool, /obj/item/storage/bag/tray))
@@ -393,6 +573,7 @@
icon_state = "rollingtable"
/// Lazylist of the items that we have on our surface.
var/list/attached_items = null
+ can_flip = FALSE
/obj/structure/table/rolling/Initialize(mapload, obj/structure/table_frame/frame_used, obj/item/stack/stack_used)
. = ..()
@@ -500,6 +681,8 @@
check_break(M)
/obj/structure/table/glass/proc/check_break(mob/living/M)
+ if(is_flipped)
+ return FALSE
if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL && !(M.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && (!isteshari(M))) //SKYRAT EDIT ADDITION - Allows Teshari to climb on glassies safely. - This should be a component
table_shatter(M)
@@ -581,15 +764,14 @@
/obj/structure/table/wood/fancy
name = "fancy table"
desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth."
- icon = 'icons/obj/structures.dmi'
- icon_state = "fancy_table"
+ icon = 'icons/obj/smooth_structures/fancy_table.dmi'
+ icon_state = "fancy_table-0"
base_icon_state = "fancy_table"
frame = /obj/structure/table_frame
framestack = /obj/item/stack/rods
buildstack = /obj/item/stack/tile/carpet
smoothing_groups = SMOOTH_GROUP_FANCY_WOOD_TABLES //Don't smooth with SMOOTH_GROUP_TABLES or SMOOTH_GROUP_WOOD_TABLES
canSmoothWith = SMOOTH_GROUP_FANCY_WOOD_TABLES
- var/smooth_icon = 'icons/obj/smooth_structures/fancy_table.dmi' // see Initialize()
/obj/structure/table/wood/fancy/Initialize(mapload, obj/structure/table_frame/frame_used, obj/item/stack/stack_used)
. = ..()
@@ -597,64 +779,63 @@
// which the editor treats as a two-tile-tall object. The sprites are that
// size so that the north/south corners look nice - examine the detail on
// the sprites in the editor to see why.
- icon = smooth_icon
/obj/structure/table/wood/fancy/apply_stack_properties(obj/item/stack/stack_used)
buildstack = stack_used.type
/obj/structure/table/wood/fancy/black
- icon_state = "fancy_table_black"
+ icon_state = "fancy_table_black-0"
base_icon_state = "fancy_table_black"
buildstack = /obj/item/stack/tile/carpet/black
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_black.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_black.dmi'
/obj/structure/table/wood/fancy/blue
- icon_state = "fancy_table_blue"
+ icon_state = "fancy_table_blue-0"
base_icon_state = "fancy_table_blue"
buildstack = /obj/item/stack/tile/carpet/blue
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_blue.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_blue.dmi'
/obj/structure/table/wood/fancy/cyan
- icon_state = "fancy_table_cyan"
+ icon_state = "fancy_table_cyan-0"
base_icon_state = "fancy_table_cyan"
buildstack = /obj/item/stack/tile/carpet/cyan
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_cyan.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_cyan.dmi'
/obj/structure/table/wood/fancy/green
- icon_state = "fancy_table_green"
+ icon_state = "fancy_table_green-0"
base_icon_state = "fancy_table_green"
buildstack = /obj/item/stack/tile/carpet/green
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_green.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_green.dmi'
/obj/structure/table/wood/fancy/orange
- icon_state = "fancy_table_orange"
+ icon_state = "fancy_table_orange-0"
base_icon_state = "fancy_table_orange"
buildstack = /obj/item/stack/tile/carpet/orange
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_orange.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_orange.dmi'
/obj/structure/table/wood/fancy/purple
- icon_state = "fancy_table_purple"
+ icon_state = "fancy_table_purple-0"
base_icon_state = "fancy_table_purple"
buildstack = /obj/item/stack/tile/carpet/purple
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_purple.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_purple.dmi'
/obj/structure/table/wood/fancy/red
- icon_state = "fancy_table_red"
+ icon_state = "fancy_table_red-0"
base_icon_state = "fancy_table_red"
buildstack = /obj/item/stack/tile/carpet/red
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_red.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_red.dmi'
/obj/structure/table/wood/fancy/royalblack
- icon_state = "fancy_table_royalblack"
+ icon_state = "fancy_table_royalblack-0"
base_icon_state = "fancy_table_royalblack"
buildstack = /obj/item/stack/tile/carpet/royalblack
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblack.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_royalblack.dmi'
/obj/structure/table/wood/fancy/royalblue
- icon_state = "fancy_table_royalblue"
+ icon_state = "fancy_table_royalblue-0"
base_icon_state = "fancy_table_royalblue"
buildstack = /obj/item/stack/tile/carpet/royalblue
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblue.dmi'
+ icon = 'icons/obj/smooth_structures/fancy_table_royalblue.dmi'
/*
* Reinforced tables
@@ -670,6 +851,7 @@
max_integrity = 200
integrity_failure = 0.25
armor_type = /datum/armor/table_reinforced
+ can_flip = FALSE
/datum/armor/table_reinforced
melee = 10
@@ -767,6 +949,7 @@
buildstack = /obj/item/stack/sheet/bronze
smoothing_groups = SMOOTH_GROUP_BRONZE_TABLES //Don't smooth with SMOOTH_GROUP_TABLES
canSmoothWith = SMOOTH_GROUP_BRONZE_TABLES
+ can_flip = FALSE
/obj/structure/table/bronze/tablepush(mob/living/user, mob/living/pushed_mob)
..()
@@ -827,6 +1010,7 @@
can_buckle = TRUE
buckle_lying = 90
custom_materials = list(/datum/material/silver =SHEET_MATERIAL_AMOUNT)
+ can_flip = FALSE
var/mob/living/carbon/patient = null
var/obj/machinery/computer/operating/computer = null
diff --git a/code/modules/antagonists/abductor/abductor_structures.dm b/code/modules/antagonists/abductor/abductor_structures.dm
index 3ca2fbacdc6..4668a67b65b 100644
--- a/code/modules/antagonists/abductor/abductor_structures.dm
+++ b/code/modules/antagonists/abductor/abductor_structures.dm
@@ -35,6 +35,7 @@
canSmoothWith = SMOOTH_GROUP_ABDUCTOR_TABLES
frame = /obj/structure/table_frame/abductor
custom_materials = list(/datum/material/silver =SHEET_MATERIAL_AMOUNT)
+ can_flip = FALSE
/obj/structure/table/optable/abductor
name = "alien operating table"
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index a770d7f4bbf..be27975294b 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -202,6 +202,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/survival_pod/left, 0)
smoothing_flags = NONE
smoothing_groups = null
canSmoothWith = null
+ can_flip = FALSE
//Sleeper
/obj/machinery/sleeper/survival_pod
diff --git a/icons/obj/flipped_tables.dmi b/icons/obj/flipped_tables.dmi
new file mode 100644
index 00000000000..46934ae7d04
Binary files /dev/null and b/icons/obj/flipped_tables.dmi differ