Files
Casper3667 ee25c1fac5 Make clothing racks and retail shelves craftable (#21434)
Allows the clothing rack and retail shelf to be crafted.
Additionally it fixes the duplicate rods from taking apart tables and
the racks giving rods when they should give plastic.
2025-10-05 17:25:58 +00:00

486 lines
16 KiB
Plaintext

/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
breakable = TRUE
build_amt = 1
//Preset shit
var/table_mat
var/table_reinf
var/no_cargo
var/flipped = 0
var/maxhealth = 10
var/health = 10
// For racks (which cannot be either of these things)
var/can_reinforce = 1
var/can_plate = 1
var/manipulating = 0
var/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/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 <b>welds</b>... no matter what its made of!"
// Needs to be plated before it can be carpeted
if(material && !carpeted)
. += "It could be surfaced with some <b>carpet</b>."
// Needs to be plated before it can be reinforced
if(material)
. += "It could be reinforced with a <b>stack</b> of an appropriate material."
// Needs to be plated before we can do much of anything
else
. += "It could be plated with a <b>stack</b> 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 <b>bolts</b>."
// Has a carpet
if(carpeted)
. += "Its carpeted surface could be <b>pried</b> loose."
// Has reinforcements
if(reinforced)
. += "Its reinforcements have been securely <b>screwed<b/> into place."
// Is not reinforced or carpeted, but is plated
else if(material && !carpeted)
. += "Its plating is secured by a couple of <b>bolts</b>."
// Table naked!!!
else if(!material)
. += "It is held together by a couple of <b>bolts</b>."
/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_name(table_mat)
if(table_reinf)
reinforced = SSmaterials.get_material_by_name(table_reinf)
if(reinforced)
breakable = FALSE
. = ..()
// 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)
breakable = FALSE
update_desc()
queue_icon_update()
update_material()
/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/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, 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')
breakable = TRUE
/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("<b>[user]</b> 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.name == material.name)
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.name == T.material.name && 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