diff --git a/baystation12.dme b/baystation12.dme
index a1074dc190..42f98251f3 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -629,7 +629,6 @@
#include "code\game\objects\items\weapons\stunbaton.dm"
#include "code\game\objects\items\weapons\surgery_tools.dm"
#include "code\game\objects\items\weapons\swords_axes_etc.dm"
-#include "code\game\objects\items\weapons\table_rack_parts.dm"
#include "code\game\objects\items\weapons\tape.dm"
#include "code\game\objects\items\weapons\teleportation.dm"
#include "code\game\objects\items\weapons\tools.dm"
@@ -718,7 +717,6 @@
#include "code\game\objects\structures\noticeboard.dm"
#include "code\game\objects\structures\safe.dm"
#include "code\game\objects\structures\signs.dm"
-#include "code\game\objects\structures\tables_racks.dm"
#include "code\game\objects\structures\tank_dispenser.dm"
#include "code\game\objects\structures\target_stake.dm"
#include "code\game\objects\structures\transit_tubes.dm"
@@ -1646,6 +1644,12 @@
#include "code\modules\surgery\robolimbs.dm"
#include "code\modules\surgery\slimes.dm"
#include "code\modules\surgery\surgery.dm"
+#include "code\modules\tables\flipping.dm"
+#include "code\modules\tables\interactions.dm"
+#include "code\modules\tables\presets.dm"
+#include "code\modules\tables\rack.dm"
+#include "code\modules\tables\tables.dm"
+#include "code\modules\tables\update_triggers.dm"
#include "code\modules\vehicles\cargo_train.dm"
#include "code\modules\vehicles\train.dm"
#include "code\modules\vehicles\vehicle.dm"
diff --git a/code/game/gamemodes/cult/cultify/obj.dm b/code/game/gamemodes/cult/cultify/obj.dm
index 630877eb11..fd239df50f 100644
--- a/code/game/gamemodes/cult/cultify/obj.dm
+++ b/code/game/gamemodes/cult/cultify/obj.dm
@@ -149,8 +149,12 @@
..()
/obj/structure/table/cultify()
- new /obj/structure/table/woodentable(loc)
- ..()
+ // Make it a wood-reinforced wooden table.
+ // There are cult materials available, but it'd make the table non-deconstructable with how holotables work.
+ // Could possibly use a new material var for holographic-ness?
+ material = name_to_material["wood"]
+ reinforced = name_to_material["wood"]
+ update_desc()
+ update_connections(1)
+ update_icon()
-/obj/structure/table/woodentable/cultify()
- return
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index a4d9e631e8..bbb8e32736 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -20,6 +20,7 @@
var/created_window = /obj/structure/window/basic
var/is_reinforced = 0
var/list/construction_options = list("One Direction", "Full Window")
+ sheettype = "glass"
/obj/item/stack/sheet/glass/cyborg
name = "glass synthesizer"
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 46415bb8af..1e6fd887f5 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -134,6 +134,7 @@ obj/item/stack/sheet/mineral/iron/New()
icon_state = "sheet-plastic"
origin_tech = "materials=3"
perunit = 2000
+ sheettype = "plastic"
/obj/item/stack/sheet/mineral/plastic/New()
..()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 1fefeca1ab..bf35253b08 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -31,8 +31,8 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \
new/datum/stack_recipe("green comfy chair", /obj/structure/bed/chair/comfy/green, 2, one_per_turf = 1, on_floor = 1), \
), 2), \
null, \
- new/datum/stack_recipe("table parts", /obj/item/weapon/table_parts, 2), \
- new/datum/stack_recipe("rack parts", /obj/item/weapon/table_parts/rack), \
+ new/datum/stack_recipe("table frame", /obj/structure/table, 1, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("rack", /obj/structure/table/rack, 1, time = 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("metal baseball bat", /obj/item/weapon/twohanded/baseballbat/metal, 10, time = 20, one_per_turf = 0, on_floor = 1), \
new/datum/stack_recipe("closet", /obj/structure/closet, 2, time = 15, one_per_turf = 1, on_floor = 1), \
null, \
@@ -163,6 +163,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \
singular_name = "wood plank"
icon_state = "sheet-wood"
origin_tech = "materials=1;biotech=1"
+ sheettype = "wood"
/obj/item/stack/sheet/wood/cyborg
name = "wood synthesizer"
diff --git a/code/game/objects/items/weapons/table_rack_parts.dm b/code/game/objects/items/weapons/table_rack_parts.dm
deleted file mode 100644
index 8437a5e29f..0000000000
--- a/code/game/objects/items/weapons/table_rack_parts.dm
+++ /dev/null
@@ -1,101 +0,0 @@
-// Table parts and rack parts
-
-/obj/item/weapon/table_parts
- name = "table parts"
- desc = "Parts of a table. Poor table."
- gender = PLURAL
- icon = 'icons/obj/items.dmi'
- icon_state = "table_parts"
- matter = list(DEFAULT_WALL_MATERIAL = 3750)
- flags = CONDUCT
- attack_verb = list("slammed", "bashed", "battered", "bludgeoned", "thrashed", "whacked")
-
- var/build_type = /obj/structure/table
- var/alter_type = /obj/item/weapon/table_parts/reinforced
- var/alter_with = /obj/item/stack/rods
- var/alter_cost = 4
- var/list/stack_types = list(/obj/item/stack/sheet/metal)
-
-/obj/item/weapon/table_parts/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/wrench))
- for(var/material_type in stack_types)
- new material_type(get_turf(user))
- qdel(src)
- return
- else
- if(alter_type && alter_with && istype(W,alter_with))
- var/obj/item/stack/R = W
- if (R.use(alter_cost))
- var/obj/item/new_parts = new alter_type (get_turf(loc))
- user << "You modify \the [name] into \a [new_parts]."
- qdel(src)
- else
- user << "You need at least [alter_cost] sheets to reinforce the [name]."
- return
- ..()
-
-/obj/item/weapon/table_parts/attack_self(mob/user as mob)
- if(locate(/obj/structure/table) in user.loc)
- user << "There is already a table here."
- return
-
- new build_type( user.loc )
- user.drop_item()
- qdel(src)
- return
-
-/obj/item/weapon/table_parts/reinforced
- name = "reinforced table parts"
- desc = "Hard table parts. Well... harder."
- icon = 'icons/obj/items.dmi'
- icon_state = "reinf_tableparts"
- matter = list(DEFAULT_WALL_MATERIAL = 7500)
- flags = CONDUCT
-
- stack_types = list(/obj/item/stack/sheet/metal, /obj/item/stack/rods)
- build_type = /obj/structure/table/reinforced
- alter_type = null
- alter_with = null
- alter_cost = null
-
-/obj/item/weapon/table_parts/wood
- name = "wooden table parts"
- desc = "Keep away from fire."
- icon_state = "wood_tableparts"
- flags = null
-
- stack_types = list(/obj/item/stack/sheet/wood)
- build_type = /obj/structure/table/woodentable
- alter_type = /obj/item/weapon/table_parts/gambling
- alter_with = /obj/item/stack/tile/carpet
- alter_cost = 1
-
-/obj/item/weapon/table_parts/gambling
- name = "gambling table parts"
- desc = "Keep away from security."
- icon_state = "gamble_tableparts"
- flags = null
-
- stack_types = list(/obj/item/stack/tile/carpet,/obj/item/stack/sheet/wood)
- build_type = /obj/structure/table/gamblingtable
- alter_type = null
- alter_with = null
- alter_cost = null
-
-/obj/item/weapon/table_parts/gambling/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/crowbar))
- new /obj/item/stack/tile/carpet( get_turf(loc) )
- new /obj/item/weapon/table_parts/wood( get_turf(loc) )
- user << "You pry the carpet out of the table."
- qdel(src)
- ..()
-
-/obj/item/weapon/table_parts/rack
- name = "rack parts"
- desc = "Parts of a rack."
- icon_state = "rack_parts"
- stack_types = list(/obj/item/stack/sheet/metal)
- build_type = /obj/structure/table/rack
- alter_type = null
- alter_with = null
- alter_cost = null
\ No newline at end of file
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
deleted file mode 100644
index f206bfc11f..0000000000
--- a/code/game/objects/structures/tables_racks.dm
+++ /dev/null
@@ -1,558 +0,0 @@
-// Tables and racks.
-
-/obj/structure/table
- name = "table"
- desc = "A square piece of metal standing on four metal legs. It can not move."
- icon = 'icons/obj/structures.dmi'
- icon_state = "table"
- density = 1
- anchored = 1
- layer = 2.8
- throwpass = 1
- climbable = 1
- breakable = 1
- parts = /obj/item/weapon/table_parts
-
- var/flipped = 0
- var/health = 100
-
-/obj/structure/table/woodentable
- name = "wooden table"
- desc = "Do not apply fire to this. Rumour says it burns easily."
- icon_state = "wood_table"
- parts = /obj/item/weapon/table_parts/wood
- health = 50
-
-/obj/structure/table/gamblingtable
- name = "gambling table"
- desc = "A curved wooden table with a thin carpet of green fabric."
- icon_state = "gamble_table"
- parts = /obj/item/weapon/table_parts/gambling
- health = 50
-
-/obj/structure/table/reinforced
- icon_state = "reinf_table"
- health = 200
- parts = /obj/item/weapon/table_parts/reinforced
-
-/obj/structure/table/rack
- name = "rack"
- desc = "Different from the Middle Ages version."
- icon = 'icons/obj/objects.dmi'
- icon_state = "rack"
- health = 100
- parts = /obj/item/weapon/table_parts/rack
- flipped = -1 //Cannot flip.
-
-/obj/structure/table/examine()
- ..()
- if(health > 100)
- usr << "This one looks like it has been reinforced."
-
-/obj/structure/table/proc/update_adjacent()
- for(var/direction in list(1,2,4,8,5,6,9,10))
- if(locate(/obj/structure/table,get_step(src,direction)))
- var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,direction))
- T.update_icon()
-
-/obj/structure/table/New()
- ..()
- for(var/obj/structure/table/T in src.loc)
- if(T != src)
- qdel(T)
- update_icon()
- update_adjacent()
-
-/obj/structure/table/Destroy()
- update_adjacent()
- ..()
-
-/obj/structure/table/update_icon()
-
- if(health > 100)
- name = "reinforced [initial(name)]"
-
- spawn(2) //So it properly updates when deleting
-
- if(flipped == 1)
- var/type = 0
- var/tabledirs = 0
- for(var/direction in list(turn(dir,90), turn(dir,-90)) )
- var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,direction))
- if (T && T.flipped == 1 && T.dir == src.dir)
- type++
- tabledirs |= direction
- var/base = "table"
- if (istype(src, /obj/structure/table/woodentable))
- base = "wood"
- if (istype(src, /obj/structure/table/reinforced))
- base = "rtable"
-
- icon_state = "[base]flip[type]"
- if (type==1)
- if (tabledirs & turn(dir,90))
- icon_state = icon_state+"-"
- if (tabledirs & turn(dir,-90))
- icon_state = icon_state+"+"
- return 1
-
- var/dir_sum = 0
- for(var/direction in list(1,2,4,8,5,6,9,10))
- var/skip_sum = 0
- for(var/obj/structure/window/W in src.loc)
- if(W.dir == direction) //So smooth tables don't go smooth through windows
- skip_sum = 1
- continue
- var/inv_direction //inverse direction
- switch(direction)
- if(1)
- inv_direction = 2
- if(2)
- inv_direction = 1
- if(4)
- inv_direction = 8
- if(8)
- inv_direction = 4
- if(5)
- inv_direction = 10
- if(6)
- inv_direction = 9
- if(9)
- inv_direction = 6
- if(10)
- inv_direction = 5
- for(var/obj/structure/window/W in get_step(src,direction))
- if(W.dir == inv_direction) //So smooth tables don't go smooth through windows when the window is on the other table's tile
- skip_sum = 1
- continue
- if(!skip_sum) //means there is a window between the two tiles in this direction
- var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,direction))
- if(T && T.flipped == 0) // This should let us ignore racks for table icons/flipping. Should.
- if(direction <5)
- dir_sum += direction
- else
- if(direction == 5) //This permits the use of all table directions. (Set up so clockwise around the central table is a higher value, from north)
- dir_sum += 16
- if(direction == 6)
- dir_sum += 32
- if(direction == 8) //Aherp and Aderp. Jezes I am stupid. -- SkyMarshal
- dir_sum += 8
- if(direction == 10)
- dir_sum += 64
- if(direction == 9)
- dir_sum += 128
-
- var/table_type = 0 //stand_alone table
- if(dir_sum%16 in cardinal)
- table_type = 1 //endtable
- dir_sum %= 16
- if(dir_sum%16 in list(3,12))
- table_type = 2 //1 tile thick, streight table
- if(dir_sum%16 == 3) //3 doesn't exist as a dir
- dir_sum = 2
- if(dir_sum%16 == 12) //12 doesn't exist as a dir.
- dir_sum = 4
- if(dir_sum%16 in list(5,6,9,10))
- if(locate(/obj/structure/table,get_step(src.loc,dir_sum%16)))
- table_type = 3 //full table (not the 1 tile thick one, but one of the 'tabledir' tables)
- else
- table_type = 2 //1 tile thick, corner table (treated the same as streight tables in code later on)
- dir_sum %= 16
- if(dir_sum%16 in list(13,14,7,11)) //Three-way intersection
- table_type = 5 //full table as three-way intersections are not sprited, would require 64 sprites to handle all combinations. TOO BAD -- SkyMarshal
- switch(dir_sum%16) //Begin computation of the special type tables. --SkyMarshal
- if(7)
- if(dir_sum == 23)
- table_type = 6
- dir_sum = 8
- else if(dir_sum == 39)
- dir_sum = 4
- table_type = 6
- else if(dir_sum == 55 || dir_sum == 119 || dir_sum == 247 || dir_sum == 183)
- dir_sum = 4
- table_type = 3
- else
- dir_sum = 4
- if(11)
- if(dir_sum == 75)
- dir_sum = 5
- table_type = 6
- else if(dir_sum == 139)
- dir_sum = 9
- table_type = 6
- else if(dir_sum == 203 || dir_sum == 219 || dir_sum == 251 || dir_sum == 235)
- dir_sum = 8
- table_type = 3
- else
- dir_sum = 8
- if(13)
- if(dir_sum == 29)
- dir_sum = 10
- table_type = 6
- else if(dir_sum == 141)
- dir_sum = 6
- table_type = 6
- else if(dir_sum == 189 || dir_sum == 221 || dir_sum == 253 || dir_sum == 157)
- dir_sum = 1
- table_type = 3
- else
- dir_sum = 1
- if(14)
- if(dir_sum == 46)
- dir_sum = 1
- table_type = 6
- else if(dir_sum == 78)
- dir_sum = 2
- table_type = 6
- else if(dir_sum == 110 || dir_sum == 254 || dir_sum == 238 || dir_sum == 126)
- dir_sum = 2
- table_type = 3
- else
- dir_sum = 2 //These translate the dir_sum to the correct dirs from the 'tabledir' icon_state.
- if(dir_sum%16 == 15)
- table_type = 4 //4-way intersection, the 'middle' table sprites will be used.
-
- if(istype(src,/obj/structure/table/reinforced))
- switch(table_type)
- if(0)
- icon_state = "reinf_table"
- if(1)
- icon_state = "reinf_1tileendtable"
- if(2)
- icon_state = "reinf_1tilethick"
- if(3)
- icon_state = "reinf_tabledir"
- if(4)
- icon_state = "reinf_middle"
- if(5)
- icon_state = "reinf_tabledir2"
- if(6)
- icon_state = "reinf_tabledir3"
- else if(istype(src,/obj/structure/table/woodentable))
- switch(table_type)
- if(0)
- icon_state = "wood_table"
- if(1)
- icon_state = "wood_1tileendtable"
- if(2)
- icon_state = "wood_1tilethick"
- if(3)
- icon_state = "wood_tabledir"
- if(4)
- icon_state = "wood_middle"
- if(5)
- icon_state = "wood_tabledir2"
- if(6)
- icon_state = "wood_tabledir3"
- else if(istype(src,/obj/structure/table/gamblingtable))
- switch(table_type)
- if(0)
- icon_state = "gamble_table"
- if(1)
- icon_state = "gamble_1tileendtable"
- if(2)
- icon_state = "gamble_1tilethick"
- if(3)
- icon_state = "gamble_tabledir"
- if(4)
- icon_state = "gamble_middle"
- if(5)
- icon_state = "gamble_tabledir2"
- if(6)
- icon_state = "gamble_tabledir3"
- else
- switch(table_type)
- if(0)
- icon_state = "table"
- if(1)
- icon_state = "table_1tileendtable"
- if(2)
- icon_state = "table_1tilethick"
- if(3)
- icon_state = "tabledir"
- if(4)
- icon_state = "table_middle"
- if(5)
- icon_state = "tabledir2"
- if(6)
- icon_state = "tabledir3"
- if (dir_sum in list(1,2,4,8,5,6,9,10))
- set_dir(dir_sum)
- else
- set_dir(2)
-
-/obj/structure/table/attack_tk() // no telehulk sorry
- return
-
-/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
- if(istype(mover,/obj/item/projectile))
- return (check_cover(mover,target))
- if(istype(mover) && mover.checkpass(PASSTABLE))
- return 1
- if(locate(/obj/structure/table) in get_turf(mover))
- return 1
- if (flipped == 1)
- if (get_dir(loc, target) == dir)
- return !density
- else
- return 1
- return 0
-
-//checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops.
-/obj/structure/table/proc/check_cover(obj/item/projectile/P, turf/from)
- var/turf/cover
- if(flipped==1)
- cover = get_turf(src)
- else if(flipped==0)
- cover = get_step(loc, get_dir(from, loc))
- if(!cover)
- return 1
- if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close
- return 1
- if (get_turf(P.original) == cover)
- var/chance = 20
- if (ismob(P.original))
- var/mob/M = P.original
- if (M.lying)
- chance += 20 //Lying down lets you catch less bullets
- if(flipped==1)
- if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets
- chance += 20
- else
- return 1 //But only from one side
- if(prob(chance))
- health -= P.damage/2
- if (health > 0)
- visible_message("[P] hits \the [src]!")
- return 0
- else
- visible_message("[src] breaks down!")
- qdel(src)
- return 1
- return 1
-
-/obj/structure/table/CheckExit(atom/movable/O as mob|obj, target as turf)
- if(istype(O) && O.checkpass(PASSTABLE))
- return 1
- if (flipped==1)
- if (get_dir(loc, target) == dir)
- return !density
- else
- return 1
- return 1
-
-/obj/structure/table/MouseDrop_T(obj/O as obj, mob/user as mob)
-
- if ((!( istype(O, /obj/item/weapon) ) || user.get_active_hand() != O))
- return ..()
- if(isrobot(user))
- return
- user.drop_item()
- if (O.loc != src.loc)
- step(O, get_dir(O, src))
- return
-
-
-/obj/structure/table/attackby(obj/item/W as obj, mob/user as mob)
- if (!W) return
-
- // Handle harm intent grabbing/tabling.
- if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
- var/obj/item/weapon/grab/G = W
- if (istype(G.affecting, /mob/living))
- var/mob/living/M = G.affecting
- if (G.state < 2)
- if(user.a_intent == I_HURT)
- if (prob(15)) M.Weaken(5)
- M.apply_damage(8,def_zone = "head")
- visible_message("[G.assailant] slams [G.affecting]'s face against \the [src]!")
- playsound(src.loc, 'sound/weapons/tablehit1.ogg', 50, 1)
- else
- user << "You need a better grip to do that!"
- return
- else
- G.affecting.loc = src.loc
- G.affecting.Weaken(5)
- visible_message("[G.assailant] puts [G.affecting] on \the [src].")
- qdel(W)
- return
-
- // Handle dissembly.
- if (istype(W, /obj/item/weapon/wrench))
- if(health > 100)
- user << "\The [src] is too well constructed to be collapsed. Weaken it first."
- return
- user << "You locate the bolts and begin disassembling \the [src]..."
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- if(do_after(user,50))
- qdel(src)
- return
-
- // Handle weakening.
- if (istype(W, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = W
- if(WT.isOn())
- if(initial(health)>100)
- if(WT.remove_fuel(0, user))
- if(src.health>100)
- user << "You start weakening \the [src]..."
- playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
- if(!do_after(user, 50) || !src || health<100 || !WT.isOn())
- return
- user << "You have weakened \the [src]."
- health -= 100
- else if(src.health <= 100)
- user << "You start strengthening \the [src]..."
- playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
- if(!do_after(user, 50) || !src || health > 100 || !WT.isOn())
- return
- user << "You have strengthened \the [src]."
- health += 100
- update_icon()
- else
- user << "\The [src] is too flimsy to be reinforced or weakened."
- return
-
-
- // Handle dismantling or placing things on the table from here on.
- if(isrobot(user))
- return
-
- if(W.loc != user) // This should stop mounted modules ending up outside the module.
- return
-
- if(istype(W, /obj/item/weapon/melee/energy/blade))
- var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
- spark_system.set_up(5, 0, src.loc)
- spark_system.start()
- playsound(src.loc, 'sound/weapons/blade1.ogg', 50, 1)
- playsound(src.loc, "sparks", 50, 1)
- user.visible_message("The [src] was sliced apart by [user]!")
- qdel(src)
-
- user.drop_item(src.loc)
- return
-
-/obj/structure/table/proc/straight_table_check(var/direction)
- var/obj/structure/table/T
- for(var/angle in list(-90,90))
- T = locate() in get_step(src.loc,turn(direction,angle))
- if(T && T.flipped == 0)
- return 0
- T = locate() in get_step(src.loc,direction)
- if (!T || T.flipped == 1)
- return 1
- if (istype(T,/obj/structure/table/reinforced/))
- var/obj/structure/table/reinforced/R = T
- if (R.health > 100)
- return 0
- return T.straight_table_check(direction)
-
-/obj/structure/table/verb/do_flip()
- set name = "Flip table"
- set desc = "Flips a non-reinforced table"
- set category = "Object"
- set src in oview(1)
-
- if (!can_touch(usr) || ismouse(usr))
- return
-
- if(flipped < 0 || !flip(get_cardinal_dir(usr,src)))
- usr << "It won't budge."
- return
-
- usr.visible_message("[usr] flips \the [src]!")
-
- if(climbable)
- structure_shaken()
-
- return
-
-/obj/structure/table/proc/unflipping_check(var/direction)
-
- for(var/mob/M in oview(src,0))
- return 0
-
- var/obj/occupied = turf_is_crowded()
- if(occupied)
- usr << "There's \a [occupied] in the way."
- return 0
-
- var/list/L = list()
- if(direction)
- L.Add(direction)
- else
- L.Add(turn(src.dir,-90))
- L.Add(turn(src.dir,90))
- for(var/new_dir in L)
- var/obj/structure/table/T = locate() in get_step(src.loc,new_dir)
- if(T)
- if(T.flipped == 1 && T.dir == src.dir && !T.unflipping_check(new_dir))
- return 0
- return 1
-
-/obj/structure/table/proc/do_put()
- set name = "Put table back"
- set desc = "Puts flipped table back"
- set category = "Object"
- set src in oview(1)
-
- if (!can_touch(usr))
- return
-
- if (!unflipping_check())
- usr << "It won't budge."
- return
- unflip()
-
-/obj/structure/table/proc/flip(var/direction)
- if( !straight_table_check(turn(direction,90)) || !straight_table_check(turn(direction,-90)) )
- return 0
-
- verbs -=/obj/structure/table/verb/do_flip
- verbs +=/obj/structure/table/proc/do_put
-
- var/list/targets = list(get_step(src,dir),get_step(src,turn(dir, 45)),get_step(src,turn(dir, -45)))
- for (var/atom/movable/A in get_turf(src))
- if (!A.anchored)
- spawn(0)
- A.throw_at(pick(targets),1,1)
-
- set_dir(direction)
- if(dir != NORTH)
- layer = 5
- climbable = 0 //flipping tables allows them to be used as makeshift barriers
- flipped = 1
- flags |= ON_BORDER
- for(var/D in list(turn(direction, 90), turn(direction, -90)))
- var/obj/structure/table/T = locate() in get_step(src,D)
- if(T && T.flipped == 0)
- T.flip(direction)
- update_icon()
- update_adjacent()
-
- return 1
-
-/obj/structure/table/proc/unflip()
- verbs -=/obj/structure/table/proc/do_put
- verbs +=/obj/structure/table/verb/do_flip
-
- layer = initial(layer)
- flipped = 0
- climbable = initial(climbable)
- flags &= ~ON_BORDER
- for(var/D in list(turn(dir, 90), turn(dir, -90)))
- var/obj/structure/table/T = locate() in get_step(src.loc,D)
- if(T && T.flipped == 1 && T.dir == src.dir)
- T.unflip()
- update_icon()
- update_adjacent()
-
- return 1
-
-// No need to handle any of this, racks are not contiguous..
-/obj/structure/table/rack/update_icon()
- return
-/obj/structure/table/rack/update_adjacent()
- return
\ No newline at end of file
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index b463ec4209..6e5c4c4c5a 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -1,5 +1,8 @@
// Holographic Items!
+// Holographic tables are in code/modules/tables/presets.dm
+// Holographic racks are in code/modules/tables/rack.dm
+
/turf/simulated/floor/holofloor/
thermal_conductivity = 0
@@ -40,36 +43,6 @@
return
// HOLOFLOOR DOES NOT GIVE A FUCK
-/obj/structure/table/holotable
- name = "table"
- desc = "A square piece of metal standing on four metal legs. It can not move."
- icon = 'icons/obj/structures.dmi'
- icon_state = "table"
- density = 1
- anchored = 1.0
- layer = 2.8
- throwpass = 1 //You can throw objects over this, despite it's density.
-
-/obj/structure/table/holotable/attack_hand(mob/user as mob)
- return // HOLOTABLE DOES NOT GIVE A FUCK
-
-
-/obj/structure/table/holotable/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/wrench))
- user << "It's a holotable! There are no bolts!"
- return
-
- if(isrobot(user))
- return
-
- ..()
-
-/obj/structure/table/woodentable/holotable
- name = "table"
- desc = "A square piece of wood standing on four wooden legs. It can not move."
- icon = 'icons/obj/structures.dmi'
- icon_state = "wood_table"
-
/obj/structure/holostool
name = "stool"
desc = "Apply butt."
@@ -371,22 +344,6 @@
for(var/mob/M in currentarea)
M << "FIGHT!"
-//Holorack
-
-/obj/structure/table/rack/holorack
- name = "rack"
- desc = "Different from the Middle Ages version."
- icon = 'icons/obj/objects.dmi'
- icon_state = "rack"
-
-/obj/structure/table/rack/holorack/attack_hand(mob/user as mob)
- return
-
-/obj/structure/table/rack/holorack/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/wrench))
- user << "It's a holorack! You can't unwrench it!"
- return
-
//Holocarp
/mob/living/simple_animal/hostile/carp/holodeck
diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm
index 04e8d485ba..55d51c772f 100644
--- a/code/modules/materials/materials.dm
+++ b/code/modules/materials/materials.dm
@@ -149,6 +149,14 @@ var/list/name_to_material
stack_type = /obj/item/stack/sheet/mineral/iron
icon_colour = "#5C5454"
+/material/wood
+ name = "wood"
+ stack_type = /obj/item/stack/sheet/wood
+ icon_colour = "#824B28"
+ integrity = 25
+ icon_base = "solid"
+ explosion_resistance = 2
+
/material/cult
name = "cult"
display_name = "disturbing stone"
@@ -167,4 +175,4 @@ var/list/name_to_material
display_name = "human remains"
/material/cult/reinf/place_dismantled_product(var/turf/target)
- new /obj/effect/decal/remains/human(target)
+ new /obj/effect/decal/remains/human(target)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index 5634cda9b6..7452514f17 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -17,8 +17,6 @@
/obj/item/weapon/module/power_control,
/obj/item/weapon/stock_parts,
/obj/item/frame,
- /obj/item/weapon/table_parts,
- /obj/item/weapon/table_parts/rack,
/obj/item/weapon/camera_assembly,
/obj/item/weapon/tank,
/obj/item/weapon/circuitboard,
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index cc58a7ce1c..c04857e972 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -238,7 +238,7 @@
D.Open()
else if(istype(A,/obj/structure/cult/pylon))
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper))
- else if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/table/rack))
+ else if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille))
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper))
Move(T)
FindTarget()
diff --git a/code/modules/tables/flipping.dm b/code/modules/tables/flipping.dm
new file mode 100644
index 0000000000..1714d390b4
--- /dev/null
+++ b/code/modules/tables/flipping.dm
@@ -0,0 +1,116 @@
+
+/obj/structure/table/proc/straight_table_check(var/direction)
+ var/obj/structure/table/T
+ for(var/angle in list(-90,90))
+ T = locate() in get_step(src.loc,turn(direction,angle))
+ if(T && T.flipped == 0 && T.material == material)
+ return 0
+ T = locate() in get_step(src.loc,direction)
+ if (!T || T.flipped == 1 || T.material != material)
+ return 1
+ if(T.health > 100)
+ return 0
+ return T.straight_table_check(direction)
+
+/obj/structure/table/verb/do_flip()
+ set name = "Flip table"
+ set desc = "Flips a non-reinforced table"
+ set category = "Object"
+ set src in oview(1)
+
+ if (!can_touch(usr) || ismouse(usr))
+ return
+
+ if(flipped < 0 || !flip(get_cardinal_dir(usr,src)))
+ usr << "It won't budge."
+ return
+
+ usr.visible_message("[usr] flips \the [src]!")
+
+ if(climbable)
+ structure_shaken()
+
+ return
+
+/obj/structure/table/proc/unflipping_check(var/direction)
+
+ for(var/mob/M in oview(src,0))
+ return 0
+
+ var/obj/occupied = turf_is_crowded()
+ if(occupied)
+ usr << "There's \a [occupied] in the way."
+ return 0
+
+ var/list/L = list()
+ if(direction)
+ L.Add(direction)
+ else
+ L.Add(turn(src.dir,-90))
+ L.Add(turn(src.dir,90))
+ for(var/new_dir in L)
+ var/obj/structure/table/T = locate() in get_step(src.loc,new_dir)
+ if(T && T.material == material)
+ if(T.flipped == 1 && T.dir == src.dir && !T.unflipping_check(new_dir))
+ return 0
+ return 1
+
+/obj/structure/table/proc/do_put()
+ set name = "Put table back"
+ set desc = "Puts flipped table back"
+ set category = "Object"
+ set src in oview(1)
+
+ if (!can_touch(usr))
+ return
+
+ if (!unflipping_check())
+ usr << "It won't budge."
+ return
+ unflip()
+
+/obj/structure/table/proc/flip(var/direction)
+ if( !straight_table_check(turn(direction,90)) || !straight_table_check(turn(direction,-90)) )
+ return 0
+
+ verbs -=/obj/structure/table/verb/do_flip
+ verbs +=/obj/structure/table/proc/do_put
+
+ var/list/targets = list(get_step(src,dir),get_step(src,turn(dir, 45)),get_step(src,turn(dir, -45)))
+ for (var/atom/movable/A in get_turf(src))
+ if (!A.anchored)
+ spawn(0)
+ A.throw_at(pick(targets),1,1)
+
+ set_dir(direction)
+ if(dir != NORTH)
+ layer = 5
+ climbable = 0 //flipping tables allows them to be used as makeshift barriers
+ flipped = 1
+ flags |= ON_BORDER
+ for(var/D in list(turn(direction, 90), turn(direction, -90)))
+ var/obj/structure/table/T = locate() in get_step(src,D)
+ if(T && T.flipped == 0 && T.material == material && material)
+ T.flip(direction)
+ update_connections(1)
+ update_icon()
+
+ return 1
+
+/obj/structure/table/proc/unflip()
+ verbs -=/obj/structure/table/proc/do_put
+ verbs +=/obj/structure/table/verb/do_flip
+
+ layer = initial(layer)
+ flipped = 0
+ climbable = initial(climbable)
+ flags &= ~ON_BORDER
+ for(var/D in list(turn(dir, 90), turn(dir, -90)))
+ var/obj/structure/table/T = locate() in get_step(src.loc,D)
+ if(T && T.flipped == 1 && T.dir == src.dir && T.material == material && material)
+ T.unflip()
+
+ update_connections(1)
+ update_icon()
+
+ return 1
\ No newline at end of file
diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm
new file mode 100644
index 0000000000..4ada40596e
--- /dev/null
+++ b/code/modules/tables/interactions.dm
@@ -0,0 +1,122 @@
+
+/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(air_group || (height==0)) return 1
+ if(istype(mover,/obj/item/projectile))
+ return (check_cover(mover,target))
+ if(istype(mover) && mover.checkpass(PASSTABLE))
+ return 1
+ if(locate(/obj/structure/table) in get_turf(mover))
+ return 1
+ if (flipped == 1)
+ if (get_dir(loc, target) == dir)
+ return !density
+ else
+ return 1
+ return 0
+
+//checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops.
+/obj/structure/table/proc/check_cover(obj/item/projectile/P, turf/from)
+ var/turf/cover
+ if(flipped==1)
+ cover = get_turf(src)
+ else if(flipped==0)
+ cover = get_step(loc, get_dir(from, loc))
+ if(!cover)
+ return 1
+ if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close
+ return 1
+ if (get_turf(P.original) == cover)
+ var/chance = 20
+ if (ismob(P.original))
+ var/mob/M = P.original
+ if (M.lying)
+ chance += 20 //Lying down lets you catch less bullets
+ if(flipped==1)
+ if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets
+ chance += 20
+ else
+ return 1 //But only from one side
+ if(prob(chance))
+ health -= P.damage/2
+ if (health > 0)
+ visible_message("[P] hits \the [src]!")
+ return 0
+ else
+ visible_message("[src] breaks down!")
+ break_to_parts()
+ return 1
+ return 1
+
+/obj/structure/table/CheckExit(atom/movable/O as mob|obj, target as turf)
+ if(istype(O) && O.checkpass(PASSTABLE))
+ return 1
+ if (flipped==1)
+ if (get_dir(loc, target) == dir)
+ return !density
+ else
+ return 1
+ return 1
+
+
+/obj/structure/table/MouseDrop_T(obj/O as obj, mob/user as mob)
+
+ if ((!( istype(O, /obj/item/weapon) ) || user.get_active_hand() != O))
+ return ..()
+ if(isrobot(user))
+ return
+ user.drop_item()
+ if (O.loc != src.loc)
+ step(O, get_dir(O, src))
+ return
+
+
+/obj/structure/table/attackby(obj/item/W as obj, mob/user as mob)
+ if (!W) return
+
+ // Handle harm intent grabbing/tabling.
+ if(istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
+ var/obj/item/weapon/grab/G = W
+ if (istype(G.affecting, /mob/living))
+ var/mob/living/M = G.affecting
+ if (G.state < 2)
+ if(user.a_intent == I_HURT)
+ if (prob(15)) M.Weaken(5)
+ M.apply_damage(8,def_zone = "head")
+ visible_message("[G.assailant] slams [G.affecting]'s face against \the [src]!")
+ playsound(src.loc, 'sound/weapons/tablehit1.ogg', 50, 1)
+ else
+ user << "You need a better grip to do that!"
+ return
+ else
+ G.affecting.loc = src.loc
+ G.affecting.Weaken(5)
+ visible_message("[G.assailant] puts [G.affecting] on \the [src].")
+ qdel(W)
+ return
+
+ // Handle dismantling or placing things on the table from here on.
+ if(isrobot(user))
+ return
+
+ if(W.loc != user) // This should stop mounted modules ending up outside the module.
+ return
+
+ if(istype(W, /obj/item/weapon/melee/energy/blade))
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, src.loc)
+ spark_system.start()
+ playsound(src.loc, 'sound/weapons/blade1.ogg', 50, 1)
+ playsound(src.loc, "sparks", 50, 1)
+ user.visible_message("\The [src] was sliced apart by [user]!")
+ break_to_parts()
+ return
+
+ if(!material)
+ user << "There's nothing to put \the [W] on! Try adding plating to \the [src] first."
+ return
+
+ user.drop_item(src.loc)
+ return
+
+/obj/structure/table/attack_tk() // no telehulk sorry
+ return
diff --git a/code/modules/tables/presets.dm b/code/modules/tables/presets.dm
new file mode 100644
index 0000000000..481897c48d
--- /dev/null
+++ b/code/modules/tables/presets.dm
@@ -0,0 +1,110 @@
+var/global/material/material_holographic_steel = null
+var/global/material/material_holographic_wood = null
+
+/obj/structure/table
+
+ standard
+ icon_state = "plain_preview"
+ color = "#666666"
+ New()
+ material = name_to_material[DEFAULT_WALL_MATERIAL]
+ ..()
+
+ reinforced
+ icon_state = "reinf_preview"
+ color = "#666666"
+ New()
+ material = name_to_material[DEFAULT_WALL_MATERIAL]
+ reinforced = name_to_material[DEFAULT_WALL_MATERIAL]
+ ..()
+
+ woodentable
+ icon_state = "plain_preview"
+ color = "#824B28"
+ New()
+ material = name_to_material["wood"]
+ ..()
+
+ gamblingtable
+ icon_state = "gamble_preview"
+ New()
+ material = name_to_material["wood"]
+ carpeted = 1
+ ..()
+
+ glass
+ icon_state = "plain_preview"
+ color = "#00E1FF"
+ alpha = 77 // 0.3 * 255
+ New()
+ material = name_to_material["glass"]
+ ..()
+
+ holotable
+ icon_state = "holo_preview"
+ color = "#666666"
+ New()
+ if(!material_holographic_steel)
+ material_holographic_steel = new /material/steel
+ material_holographic_steel.stack_type = null // Tables with null-stacktype materials cannot be deconstructed
+ material = material_holographic_steel
+ ..()
+
+ woodentable/holotable
+ icon_state = "holo_preview"
+ New()
+ if(!material_holographic_wood)
+ material_holographic_wood = new /material/wood
+ material_holographic_wood.stack_type = null // Tables with null-stacktype materials cannot be deconstructed
+ material = material_holographic_wood
+ ..()
+
+/*
+
+/obj/structure/table/holotable
+ name = "table"
+ desc = "A square piece of metal standing on four metal legs. It can not move."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "table"
+ density = 1
+ anchored = 1.0
+ layer = 2.8
+ throwpass = 1 //You can throw objects over this, despite it's density.
+
+/obj/structure/table/holotable/attack_hand(mob/user as mob)
+ return // HOLOTABLE DOES NOT GIVE A FUCK
+
+
+/obj/structure/table/holotable/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if (istype(W, /obj/item/weapon/wrench))
+ user << "It's a holotable! There are no bolts!"
+ return
+
+ if(isrobot(user))
+ return
+
+ ..()
+
+/obj/structure/table/woodentable/holotable
+ name = "table"
+ desc = "A square piece of wood standing on four wooden legs. It can not move."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "wood_table"
+
+
+/obj/structure/table/rack/holorack
+ name = "rack"
+ desc = "Different from the Middle Ages version."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "rack"
+
+/obj/structure/table/rack/holorack/attack_hand(mob/user as mob)
+ return
+
+/obj/structure/table/rack/holorack/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if (istype(W, /obj/item/weapon/wrench))
+ user << "It's a holorack! You can't unwrench it!"
+ return
+
+
+*/
diff --git a/code/modules/tables/rack.dm b/code/modules/tables/rack.dm
new file mode 100644
index 0000000000..bd3ee6ce55
--- /dev/null
+++ b/code/modules/tables/rack.dm
@@ -0,0 +1,26 @@
+/obj/structure/table/rack
+ name = "rack"
+ desc = "Different from the Middle Ages version."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "rack"
+ can_plate = 0
+ can_reinforce = 0
+ flipped = -1
+
+/obj/structure/table/rack/New()
+ ..()
+ verbs -= /obj/structure/table/verb/do_flip
+ verbs -= /obj/structure/table/proc/do_put
+
+/obj/structure/table/rack/update_connections()
+ return
+
+/obj/structure/table/rack/update_desc()
+ return
+
+/obj/structure/table/rack/update_icon()
+ return
+
+/obj/structure/table/rack/holorack/dismantle(obj/item/weapon/wrench/W, mob/user)
+ user << "You cannot dismantle \the [src]."
+ return
diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm
new file mode 100644
index 0000000000..1b389323f9
--- /dev/null
+++ b/code/modules/tables/tables.dm
@@ -0,0 +1,337 @@
+
+
+/obj/structure/table
+ name = "table frame"
+ icon = 'icons/obj/tables.dmi'
+ icon_state = "frame"
+ desc = "It's a table, for putting things on. Or standing on, if you really want to."
+ density = 1
+ anchored = 1
+ climbable = 1
+ var/flipped = 0
+ var/maxhealth = 10
+ var/health = 10
+
+ // For racks.
+ var/can_reinforce = 1
+ var/can_plate = 1
+
+ var/material/material = null
+ var/material/reinforced = null
+
+ // Gambling tables. I'd prefer reinforced with carpet/felt/cloth/whatever, but AFAIK it's either harder or impossible to get /obj/item/stack/sheet of those.
+ // Convert if/when you can easily get stacks of these.
+ var/carpeted = 0
+
+ var/list/connections = list("nw0", "ne0", "sw0", "se0")
+
+/obj/structure/table/proc/update_material()
+ var/old_maxhealth = maxhealth
+ if(!material)
+ maxhealth = 10
+ else
+ maxhealth = material.integrity / 2
+
+ if(reinforced)
+ maxhealth += reinforced.integrity / 2
+
+ health += maxhealth - old_maxhealth
+
+/obj/structure/table/New()
+ ..()
+ // One table per turf.
+ for(var/obj/structure/table/T in loc)
+ if(T != src)
+ // There's another table here that's not us, break to metal.
+ // break_to_parts calls qdel(src)
+ break_to_parts(full_return = 1)
+ return
+
+/obj/structure/table/initialize()
+ ..()
+ // reset color/alpha, since they're set for nice map previews
+ color = "#ffffff"
+ alpha = 255
+ update_connections(1)
+ update_icon()
+ update_desc()
+
+/obj/structure/table/Destroy()
+ material = null
+ update_connections(1) // Update tables around us to ignore us (material=null forces no connections)
+ for(var/obj/structure/table/T in oview(src, 1))
+ T.update_icon()
+ ..()
+
+/obj/structure/table/attackby(obj/item/weapon/W, mob/user)
+
+ if(reinforced && istype(W, /obj/item/weapon/screwdriver))
+ remove_reinforced(W, user)
+ if(!reinforced)
+ update_desc()
+ update_icon()
+ return 1
+
+ if(!reinforced && material && istype(W, /obj/item/weapon/wrench))
+ remove_material(W, user)
+ if(!material)
+ update_connections(1)
+ update_icon()
+ return 1
+
+ if(!reinforced && !material && istype(W, /obj/item/weapon/wrench))
+ dismantle(W, user)
+ return 1
+
+ if(!material && can_plate && istype(W, /obj/item/stack/sheet))
+ material = common_material_add(W, user, "plat")
+ if(material)
+ update_connections(1)
+ update_icon()
+ return 1
+
+ return ..()
+
+/obj/structure/table/MouseDrop_T(obj/item/stack/sheet/what)
+ if(can_reinforce && isliving(usr) && (!usr.stat) && istype(what) && usr.get_active_hand() == what && Adjacent(usr))
+ reinforce_table(what, usr)
+ else
+ return ..()
+
+/obj/structure/table/proc/reinforce_table(obj/item/stack/sheet/S, mob/user)
+ if(reinforced)
+ user << "\The [src] is already reinforced!"
+ return
+
+ if(!can_reinforce)
+ user << "\The [src] cannot be reinforced!"
+
+ reinforced = common_material_add(S, user, "reinforc")
+ if(reinforced)
+ update_desc()
+ update_icon()
+
+/obj/structure/table/proc/update_desc()
+ if(material)
+ name = "[material.display_name] table"
+ else
+ name = "table frame"
+
+ if(reinforced)
+ name = "reinforced [name]"
+ desc = "[initial(desc)] This one seems to be reinforced with [reinforced.display_name]."
+ else
+ desc = initial(desc)
+
+// Returns the material to set the table to.
+/obj/structure/table/proc/common_material_add(obj/item/stack/sheet/S, mob/user, verb) // Verb is actually verb without 'e' or 'ing', which is added. Works for 'plate'/'plating' and 'reinforce'/'reinforcing'.
+ var/material/M = name_to_material[S.sheettype]
+ if(!istype(M))
+ user << "You cannot [verb]e \the [src] with \the [S]."
+ return null
+ user << "You begin [verb]ing \the [src] with [M.display_name]."
+ if(!do_after(user, 20) || !S.use(1))
+ return null
+ user.visible_message("\The [user] [verb]es \the [src] with [M.display_name].", "You finish [verb]ing \the [src].")
+ return M
+
+// Returns the material to set the table to.
+/obj/structure/table/proc/common_material_remove(mob/user, material/M, delay, what, type_holding)
+ if(!M.stack_type)
+ user << "You are unable to remove the [what] from this table!"
+ return M
+
+ user.visible_message("\The [user] begins removing the [type_holding] holding \the [src]'s [M.display_name] [what] in place.",
+ "You begin removing the [type_holding] holding \the [src]'s [M.display_name] [what] in place.")
+ if(!do_after(user, 40))
+ return M
+ user.visible_message("\The [user] removes the [M.display_name] [what] from \the [src].",
+ "You remove the [M.display_name] [what] from \the [src].")
+ new M.stack_type(src.loc)
+ return null
+
+/obj/structure/table/proc/remove_reinforced(obj/item/weapon/screwdriver/S, mob/user)
+ reinforced = common_material_remove(user, reinforced, 40, "reinforcements", "screws")
+
+/obj/structure/table/proc/remove_material(obj/item/weapon/wrench/W, mob/user)
+ material = common_material_remove(user, material, 20, "plating", "bolts")
+
+/obj/structure/table/proc/dismantle(obj/item/weapon/wrench/W, mob/user)
+ user.visible_message("\The [user] begins dismantling \the [src].",
+ "You begin dismantling \the [src].")
+ if(!do_after(user, 20))
+ return
+ user.visible_message("\The [user] dismantles \the [src].",
+ "You dismantle \the [src].")
+ new /obj/item/stack/sheet/metal(src.loc)
+
+/obj/structure/table/proc/break_to_parts(full_return = 0)
+ if(reinforced && reinforced.stack_type && (full_return || prob(25)))
+ new reinforced.stack_type(src.loc)
+ if(material && material.stack_type && (full_return || prob(50)))
+ new material.stack_type(src.loc)
+ if(full_return || prob(50))
+ new /obj/item/stack/sheet/metal(src.loc)
+ qdel(src)
+ return
+
+/obj/structure/table/update_icon()
+ if(flipped != 1)
+ icon_state = "blank"
+ overlays.Cut()
+
+ // Base frame shape. Mostly done for glass/diamond tables, where this is visible.
+ for(var/n in connections)
+ overlays += n
+
+ // Standard table image
+ if(material)
+ for(var/n in connections)
+ var/image/I = image(icon, "[material.icon_base]_[n]")
+ I.color = material.icon_colour
+ I.alpha = 255 * material.opacity
+ overlays += I
+
+ // Reinforcements
+ if(reinforced)
+ for(var/n in connections)
+ var/image/I = image(icon, "[reinforced.icon_reinf]_[n]")
+ I.color = reinforced.icon_colour
+ I.alpha = 255 * reinforced.opacity
+ overlays += I
+
+ if(carpeted)
+ for(var/n in connections)
+ overlays += "carpet_[n]"
+ else
+ overlays.Cut()
+ var/type = 0
+ var/tabledirs = 0
+ for(var/direction in list(turn(dir,90), turn(dir,-90)) )
+ var/obj/structure/table/T = locate(/obj/structure/table ,get_step(src,direction))
+ if (T && T.flipped == 1 && T.dir == src.dir && T.material == material)
+ type++
+ tabledirs |= direction
+
+ type = "[type]"
+ if (type=="1")
+ if (tabledirs & turn(dir,90))
+ type += "-"
+ if (tabledirs & turn(dir,-90))
+ type += "+"
+
+ icon_state = "flip[type]"
+ if(material)
+ var/image/I = image(icon, "[material.icon_base]_flip[type]")
+ I.color = material.icon_colour
+ I.alpha = 255 * material.opacity
+ overlays += I
+ name = "[material.display_name] table"
+ else
+ name = "table frame"
+
+ if(reinforced)
+ var/image/I = image(icon, "[reinforced.icon_reinf]_flip[type]")
+ I.color = reinforced.icon_colour
+ I.alpha = 255 * reinforced.opacity
+ overlays += I
+
+ if(carpeted)
+ for(var/n in connections)
+ overlays += "carpet_flip[type]"
+
+// set propagate if you're updating a table that should update tables around it too, for example if it's a new table or something important has changed (like material).
+/obj/structure/table/proc/update_connections(propagate=0)
+ if(!material)
+ connections = list("nw0", "ne0", "sw0", "se0")
+
+ if(propagate)
+ for(var/obj/structure/table/T in oview(src, 1))
+ T.update_connections()
+ return
+
+ var/list/blocked_dirs = list()
+ for(var/obj/structure/window/W in get_turf(src))
+ if(W.is_fulltile())
+ connections = list("nw0", "ne0", "sw0", "se0")
+ return
+ blocked_dirs |= W.dir
+
+ for(var/D in list(NORTH, SOUTH, EAST, WEST) - blocked_dirs)
+ var/turf/T = get_step(src, D)
+ for(var/obj/structure/window/W in T)
+ if(W.is_fulltile() || W.dir == reverse_dir[D])
+ blocked_dirs |= D
+ break
+ else
+ if(W.dir != D) // it's off to the side
+ blocked_dirs |= W.dir|D // blocks the diagonal
+
+ for(var/D in list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) - blocked_dirs)
+ var/turf/T = get_step(src, D)
+
+ for(var/obj/structure/window/W in T)
+ if(W.is_fulltile() || W.dir & reverse_dir[D])
+ blocked_dirs |= D
+ break
+
+ // Blocked cardinals block the adjacent diagonals too. Prevents weirdness with tables.
+ for(var/x in list(NORTH, SOUTH))
+ for(var/y in list(EAST, WEST))
+ if((x in blocked_dirs) || (y in blocked_dirs))
+ blocked_dirs |= x|y
+
+ var/list/connection_dirs = list()
+
+ for(var/obj/structure/table/T in oview(src, 1))
+ var/T_dir = get_dir(src, T)
+ if(T_dir in blocked_dirs) continue
+ if(material == T.material && flipped == T.flipped)
+ connection_dirs |= T_dir
+ if(propagate)
+ spawn(0)
+ T.update_connections()
+ T.update_icon()
+
+ connections = dirs_to_corner_states(connection_dirs)
+
+#define CORNER_NONE 0
+#define CORNER_EASTWEST 1
+#define CORNER_DIAGONAL 2
+#define CORNER_NORTHSOUTH 4
+
+/proc/dirs_to_corner_states(list/dirs)
+ if(!istype(dirs)) return
+
+ var/NE = CORNER_NONE
+ var/NW = CORNER_NONE
+ var/SE = CORNER_NONE
+ var/SW = CORNER_NONE
+
+ if(NORTH in dirs)
+ NE |= CORNER_NORTHSOUTH
+ NW |= CORNER_NORTHSOUTH
+ if(SOUTH in dirs)
+ SW |= CORNER_NORTHSOUTH
+ SE |= CORNER_NORTHSOUTH
+ if(EAST in dirs)
+ SE |= CORNER_EASTWEST
+ NE |= CORNER_EASTWEST
+ if(WEST in dirs)
+ NW |= CORNER_EASTWEST
+ SW |= CORNER_EASTWEST
+ if(NORTHWEST in dirs)
+ NW |= CORNER_DIAGONAL
+ if(NORTHEAST in dirs)
+ NE |= CORNER_DIAGONAL
+ if(SOUTHEAST in dirs)
+ SE |= CORNER_DIAGONAL
+ if(SOUTHWEST in dirs)
+ SW |= CORNER_DIAGONAL
+
+ return list("ne[NE]", "se[SE]", "sw[SW]", "nw[NW]")
+
+#undef CORNER_NONE
+#undef CORNER_EASTWEST
+#undef CORNER_DIAGONAL
+#undef CORNER_NORTHSOUTH
\ No newline at end of file
diff --git a/code/modules/tables/update_triggers.dm b/code/modules/tables/update_triggers.dm
new file mode 100644
index 0000000000..a90e3eeb8a
--- /dev/null
+++ b/code/modules/tables/update_triggers.dm
@@ -0,0 +1,22 @@
+/obj/structure/window/New()
+ ..()
+ for(var/obj/structure/table/T in view(src, 1))
+ T.update_connections()
+ T.update_icon()
+
+/obj/structure/window/Destroy()
+ var/oldloc = loc
+ loc=null
+ for(var/obj/structure/table/T in view(oldloc, 1))
+ T.update_connections()
+ T.update_icon()
+ loc=oldloc
+ ..()
+
+/obj/structure/window/Move()
+ var/oldloc = loc
+ . = ..()
+ if(loc != oldloc)
+ for(var/obj/structure/table/T in view(oldloc, 1) | view(loc, 1))
+ T.update_connections()
+ T.update_icon()
\ No newline at end of file
diff --git a/icons/obj/tables.dmi b/icons/obj/tables.dmi
new file mode 100644
index 0000000000..429969fc86
Binary files /dev/null and b/icons/obj/tables.dmi differ