/obj/structure/table name = "table frame" icon = 'icons/obj/structure/tables/table.dmi' icon_state = "frame" desc = "It's a table, for putting things on. Or standing on, if you really want to." density = TRUE anchored = TRUE pass_flags_self = PASSTABLE | LETPASSTHROW climbable = TRUE layer = TABLE_LAYER build_amt = 1 //Preset shit var/table_mat var/table_reinf var/no_cargo var/flipped = 0 maxhealth = 10 // For racks (which cannot be either of these things) var/can_reinforce = 1 var/can_plate = 1 var/manipulating = 0 var/singleton/material/reinforced = null var/obj/item/stack/dismantle_mat = /obj/item/stack/rods // Gambling tables. I'd prefer reinforced with carpet/felt/cloth/whatever, but AFAIK it's either harder or impossible to get /obj/item/stack/material 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/get_damage_condition_hints(mob/user, distance, is_adjacent) if(health < maxhealth) switch(health / maxhealth) if(0.0 to 0.5) . += SPAN_WARNING("It looks severely damaged!") if(0.25 to 0.5) . += SPAN_WARNING("It looks damaged!") if(0.5 to 1.0) . += SPAN_NOTICE("It has a few scrapes and dents.") /obj/structure/table/mechanics_hints() . = list() . += ..() . += "Straight tables, so long as they're not too heavy or reinforced, can be flipped over with a verb when adjacent to them!" /obj/structure/table/assembly_hints() . = list() . += ..() // Rule racks out entirely first. if(!can_reinforce || !can_plate) return FALSE if(health < maxhealth) . += "It could be repaired with a few choice welds... no matter what its made of!" // Needs to be plated before it can be carpeted if(material && !carpeted) . += "It could be surfaced with some carpet." // Needs to be plated before it can be reinforced if(material) . += "It could be reinforced with a stack of an appropriate material." // Needs to be plated before we can do much of anything else . += "It could be plated with a stack of an appropriate material." /obj/structure/table/disassembly_hints() . = list() . += ..() // Rule racks out entirely first. If we ever let them be customized/have health, update this. if(!can_reinforce || !can_plate) . += "It is held together by a couple of bolts." // Has a carpet if(carpeted) . += "Its carpeted surface could be pried loose." // Has reinforcements if(reinforced) . += "Its reinforcements have been securely screwed into place." // Is not reinforced or carpeted, but is plated else if(material && !carpeted) . += "Its plating is secured by a couple of bolts." // Table naked!!! else if(!material) . += "It is held together by a couple of bolts." /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/proc/take_damage(amount, msg = TRUE) // If the table is made of a brittle material, and is *not* reinforced with a non-brittle material, damage is multiplied by TABLE_BRITTLE_MATERIAL_MULTIPLIER if(material && material.is_brittle()) if(reinforced) if(reinforced.is_brittle()) amount *= TABLE_BRITTLE_MATERIAL_MULTIPLIER else amount *= TABLE_BRITTLE_MATERIAL_MULTIPLIER health -= amount if(health <= 0) if(msg) visible_message(SPAN_WARNING("\The [src] breaks down!"), intent_message = THUNK_SOUND) return break_to_parts() // if we break and form shards, return them to the caller to do !FUN! things with /obj/structure/table/attack_generic(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type) if(!damage || !environment_smash) return user.do_attack_animation(src) visible_message(SPAN_DANGER("[user] [attack_message] \the [src]!")) take_damage(damage) return TRUE /obj/structure/table/ex_act(severity) switch(severity) if(1.0) qdel(src) return if(2.0) take_damage(rand(100,400), FALSE) if(3.0) take_damage(rand(50,150), FALSE) /obj/structure/table/Initialize() if(table_mat) material = SSmaterials.get_material_by_id(table_mat) if(table_reinf) reinforced = SSmaterials.get_material_by_id(table_reinf) AddComponent(/datum/component/armor, list(MELEE = ARMOR_MELEE_KNIVES, BULLET = ARMOR_BALLISTIC_MINOR)) . = ..() // 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 // reset color/alpha and icon, since they're set for nice map previews icon = 'icons/obj/structure/tables/table.dmi' color = "#ffffff" alpha = 255 update_connections(1) queue_icon_update() update_desc() update_material() var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) /obj/structure/table/Destroy() material = null reinforced = 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.queue_icon_update() return ..() /obj/structure/table/proc/reinforce_table(obj/item/stack/material/S, mob/user) if(reinforced) to_chat(user, SPAN_WARNING("\The [src] is already reinforced!")) return if(!can_reinforce) to_chat(user, SPAN_WARNING("\The [src] cannot be reinforced!")) return if(!material) to_chat(user, SPAN_WARNING("Plate \the [src] before reinforcing it!")) return if(flipped) to_chat(user, SPAN_WARNING("Put \the [src] back in place before reinforcing it!")) return reinforced = common_material_add(S, user, "reinforc") if(reinforced) update_desc() queue_icon_update() update_material() AddComponent(/datum/component/armor, list(MELEE = ARMOR_MELEE_KNIVES, BULLET = ARMOR_BALLISTIC_MINOR)) /obj/structure/table/proc/update_desc() if(material) if(material_alteration & MATERIAL_ALTERATION_NAME) 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/material/S, mob/user, verb) // Verb is actually verb without 'e' or 'ing', which is added. Works for 'plate'/'plating' and 'reinforce'/'reinforcing'. var/singleton/material/M = S.get_material() if(!istype(M)) to_chat(user, SPAN_WARNING("You cannot [verb]e \the [src] with \the [S].")) return null if(manipulating) return M manipulating = 1 to_chat(user, SPAN_NOTICE("You begin [verb]ing \the [src] with [M.display_name].")) if(!do_after(user, 2 SECONDS, src, DO_REPAIR_CONSTRUCT) || !S.use(1)) manipulating = 0 return null user.visible_message(SPAN_NOTICE("\The [user] [verb]es \the [src] with [M.display_name]."), SPAN_NOTICE("You finish [verb]ing \the [src].")) manipulating = 0 return M // Returns the material to set the table to. /obj/structure/table/proc/common_material_remove(mob/user, singleton/material/M, delay, what, type_holding, sound) if(!M.stack_type) to_chat(user, SPAN_WARNING("You are unable to remove the [what] from this table!")) return M if(manipulating) return M manipulating = 1 user.visible_message(SPAN_NOTICE("\The [user] begins removing the [type_holding] holding \the [src]'s [M.display_name] [what] in place."), SPAN_NOTICE("You begin removing the [type_holding] holding \the [src]'s [M.display_name] [what] in place.")) if(sound) playsound(src.loc, sound, 50, 1) if(!do_after(user, 4 SECONDS, src, DO_REPAIR_CONSTRUCT)) manipulating = 0 return M user.visible_message(SPAN_NOTICE("\The [user] removes the [M.display_name] [what] from \the [src]."), SPAN_NOTICE("You remove the [M.display_name] [what] from \the [src].")) new M.stack_type(src.loc) manipulating = 0 return null /obj/structure/table/proc/remove_reinforced(obj/item/screwdriver/S, mob/user) reinforced = common_material_remove(user, reinforced, 40, "reinforcements", "screws", 'sound/items/Screwdriver.ogg') /obj/structure/table/proc/remove_material(obj/item/wrench/W, mob/user) material = common_material_remove(user, material, 20, "plating", "bolts", W.usesound) /obj/structure/table/dismantle(obj/item/wrench/W, mob/user) if(manipulating) return manipulating = TRUE user.visible_message("[user] begins dismantling \the [src].", SPAN_NOTICE("You begin dismantling \the [src].")) if(!W.use_tool(src, user, 20, volume = 50)) manipulating = FALSE return user.visible_message("\The [user] dismantles \the [src].", SPAN_NOTICE("You dismantle \the [src].")) new dismantle_mat(src.loc) qdel(src) // Returns a list of /obj/item/material/shard objects that were created as a result of this table's breakage. // Used for !fun! things such as embedding shards in the faces of tableslammed people. // The repeated // S = [x].place_shard(loc) // if(S) shards += S // is to avoid filling the list with nulls, as place_shard won't place shards of certain materials (holo-wood, holo-steel) /obj/structure/table/proc/break_to_parts(full_return = 0) var/list/shards = list() var/obj/item/material/shard/S = null if(reinforced) if(reinforced.stack_type && (full_return || prob(20))) reinforced.place_sheet(loc) else S = reinforced.place_shard(loc) if(S) shards += S if(material) if(material?.stack_type && (full_return || prob(20))) material.place_sheet(loc) else S = material.place_shard(loc) if(S) shards += S if(carpeted && (full_return || prob(50))) // Higher chance to get the carpet back intact, since there's no non-intact option new /obj/item/stack/tile/carpet(src.loc) if(full_return || prob(20)) new dismantle_mat(src.loc) qdel(src) return shards /obj/structure/table/update_icon() ClearOverlays() icon_state = "blank" var/image/I if(flipped != 1) if(material) // Standard table image. for(var/i = 1 to 4) if(material.table_icon) if(reinforced && ("reinf_[material.icon_base]_[connections[i]]" in icon_states(material.table_icon))) I = image(material.table_icon, "reinf_[material.icon_base]_[connections[i]]", dir = 1<<(i-1)) else I = image(material.table_icon, "[material.icon_base]_[connections[i]]", dir = 1<<(i-1)) else if(reinforced && ("reinf_[material.icon_base]_[connections[i]]" in icon_states(icon))) I = image(icon, "reinf_[material.icon_base]_[connections[i]]", dir = 1<<(i-1)) else I = image(icon, "[material.icon_base]_[connections[i]]", dir = 1<<(i-1)) if(material.icon_colour) I.color = material.icon_colour AddOverlays(I) else // Table frame for(var/i = 1 to 4) I = image(icon, "[connections[i]]", dir = 1<<(i-1)) AddOverlays(I) if(reinforced) // Reinforcements. for(var/i = 1 to 4) if("reinf_[reinforced.name]_[connections[i]]" in icon_states('icons/obj/structure/tables/table_reinf.dmi')) // if it's got an existing purpose-made reinforced icon, and it isn't already a generic one, use it I = image('icons/obj/structure/tables/table_reinf.dmi', "reinf_[reinforced.name]_[connections[i]]", dir = 1<<(i-1)) else I = image('icons/obj/structure/tables/table_reinf.dmi', "[reinforced.reinf_icon]_[connections[i]]", dir = 1<<(i-1)) // else use the generic recolorable one if(reinforced.icon_colour && ("reinf_[reinforced.name]" == "[reinforced.reinf_icon]")) I.color = reinforced.icon_colour AddOverlays(I) if(carpeted) for(var/i = 1 to 4) I = image(icon, "carpet_[connections[i]]", dir = 1<<(i-1)) AddOverlays(I) else // Flipped table 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 && material && T.material && T.material.type == material.type) type++ tabledirs |= direction type = "[type]" if(type=="1") if(tabledirs & turn(dir,90)) type += "-" if(tabledirs & turn(dir,-90)) type += "+" if(material) if(material.table_icon) if(reinforced && ("reinf_[material.name]_flip[type]" in icon_states(material.table_icon))) I = image(material.table_icon, "reinf_[material.name]_flip[type]") else I = image(material.table_icon, "[material.name]_flip[type]") else if(reinforced && ("reinf_[material.icon_base]_flip[type]" in icon_states(icon))) I = image(icon, "reinf_[material.icon_base]_flip[type]") else I = image(icon, "[material.icon_base]_flip[type]") if(material.icon_colour) I.color = material.icon_colour AddOverlays(I) if(material.display_name) if(material.display_name == "comfy") name = "fancy table" else name = "[material.display_name] table" else I = image(icon, "flip[type]") name = "table frame" AddOverlays(I) if(reinforced) if("reinf_[reinforced.name]_flip[type]" in icon_states('icons/obj/structure/tables/table_reinf.dmi')) // if it's got an existing purpose-made reinforced icon, and it isn't already a generic one, use it I = image('icons/obj/structure/tables/table_reinf.dmi', "reinf_[reinforced.name]_flip[type]") else I = image('icons/obj/structure/tables/table_reinf.dmi', "[reinforced.reinf_icon]_flip[type]") // else use the generic recolorable one if(reinforced.icon_colour && ("reinf_[reinforced.name]" == "[reinforced.reinf_icon]")) I.color = reinforced.icon_colour AddOverlays(I) if(carpeted) AddOverlays("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("0", "0", "0", "0") 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("0", "0", "0", "0") 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 orange(src, 1)) var/T_dir = get_dir(src, T) if(T_dir in blocked_dirs) continue if(material && T.material && material.type == T.material.type && flipped == T.flipped) connection_dirs |= T_dir if(propagate) INVOKE_ASYNC(T, PROC_REF(update_connections)) INVOKE_ASYNC(T, TYPE_PROC_REF(/atom, queue_icon_update)) connections = dirs_to_corner_states(connection_dirs) #define CORNER_NONE 0 #define CORNER_COUNTERCLOCKWISE 1 #define CORNER_DIAGONAL 2 #define CORNER_CLOCKWISE 4 /* turn() is weird: turn(icon, angle) turns icon by angle degrees clockwise turn(matrix, angle) turns matrix by angle degrees clockwise turn(dir, angle) turns dir by angle degrees counter-clockwise */ /proc/dirs_to_corner_states(list/dirs) if(!istype(dirs)) return var/list/ret = list(NORTHWEST, SOUTHEAST, NORTHEAST, SOUTHWEST) for(var/i = 1 to ret.len) var/dir = ret[i] . = CORNER_NONE if(dir in dirs) . |= CORNER_DIAGONAL if(turn(dir,45) in dirs) . |= CORNER_COUNTERCLOCKWISE if(turn(dir,-45) in dirs) . |= CORNER_CLOCKWISE ret[i] = "[.]" return ret #undef CORNER_NONE #undef CORNER_COUNTERCLOCKWISE #undef CORNER_DIAGONAL #undef CORNER_CLOCKWISE