Files
Batrachophreno 4ecb0bc21c Repath obj/machinery to obj/structure/machinery [MDB Ignore] (#22500)
Repaths obj/machinery to obj/structure/machinery. **Note for
reviewers:** the only meaningful changed code exists within
**code/game/objects/structures.dm** and
**code/game/objects/structures/_machinery.dm**, largely concerning
damage procs. With the exception of moving airlock defines to their own
file, ALL OTHER CHANGES ARE STRICTLY PATH CHANGES.

Objects, _categorically_, are largely divided between those you can hold
in your hand/inventory and those you can't. Machinery objects are
already subtypes of Structures behaviorally, this PR just makes their
pathing reflect that, and allows for future work (tool actions, more
health/destruction functionality) to be developed without unnecessary
code duplication.

I have tested this PR by loading up the Horizon and dismantling various
machines and structures with tools, shooting guns of various types
throughout the ship, and detonating a bunch of explosions throughout the
ship.
2026-05-26 19:35:48 +00:00

376 lines
13 KiB
Plaintext

/* Library Items
*
* Contains:
* * Bookcase
* * Book
* * Barcode Scanner
*/
/*
* Bookcase
*/
/obj/structure/bookcase
name = "bookcase"
icon = 'icons/obj/library.dmi'
icon_state = "book-0"
anchored = 1
density = 1
build_amt = 5
/obj/structure/bookcase/Initialize()
. = ..()
for(var/obj/item/I in loc)
if(istype(I, /obj/item/book))
I.forceMove(src)
update_icon()
/obj/structure/bookcase/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/book))
user.drop_from_inventory(attacking_item,src)
update_icon()
else if(attacking_item.tool_behaviour == TOOL_PEN)
var/newname = sanitizeSafe(input("What would you like to title this bookshelf?"), MAX_NAME_LEN)
if(!newname)
return
else
name = ("bookcase ([newname])")
else if(attacking_item.tool_behaviour == TOOL_WRENCH)
attacking_item.play_tool_sound(get_turf(src), 100)
to_chat(user, (anchored ? SPAN_NOTICE("You unfasten \the [src] from the floor.") : SPAN_NOTICE("You secure \the [src] to the floor.")))
anchored = !anchored
else if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
to_chat(user, SPAN_NOTICE("You begin dismantling \the [src]."))
if(attacking_item.use_tool(src, user, 25, volume = 50))
to_chat(user, SPAN_NOTICE("You dismantle \the [src]."))
for(var/obj/item/book/b in contents)
b.forceMove((get_turf(src)))
dismantle()
else
..()
/obj/structure/bookcase/attack_hand(var/mob/user)
if(contents.len)
var/obj/item/book/choice = tgui_input_list(user, "Which book would you like to remove from the shelf?", "Bookcase", contents)
if(choice)
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
return
if(ishuman(user))
if(!user.get_active_hand())
user.put_in_hands(choice)
else
choice.forceMove(get_turf(src))
update_icon()
/obj/structure/bookcase/ex_act(severity)
switch(severity)
if(1.0)
for(var/obj/item/book/b in contents)
qdel(b)
qdel(src)
return
if(2.0)
for(var/obj/item/book/b in contents)
if (prob(50)) b.forceMove(get_turf(src))
else qdel(b)
qdel(src)
return
if(3.0)
if (prob(50))
for(var/obj/item/book/b in contents)
b.forceMove(get_turf(src))
qdel(src)
return
return
/obj/structure/bookcase/update_icon()
if(contents.len < 5)
icon_state = "book-[contents.len]"
else
icon_state = "book-5"
/obj/structure/bookcase/libraryspawn
var/spawn_category
var/spawn_amount = 3
/obj/structure/bookcase/libraryspawn/Initialize()
. = ..()
name = "[initial(name)] ([spawn_category])"
addtimer(CALLBACK(src, PROC_REF(populate_shelves)), 1)
/obj/structure/bookcase/libraryspawn/proc/populate_shelves()
if (!establish_db_connection(GLOB.dbcon))
return
var/query_str = "SELECT author, title, content FROM ss13_library ORDER BY RAND() LIMIT :amount:"
var/list/query_data = list("amount" = spawn_amount)
if (spawn_category)
query_str = "SELECT author, title, content FROM ss13_library WHERE category = :cat: ORDER BY RAND() LIMIT :amount:"
query_data["cat"] = spawn_category
var/DBQuery/query_books = GLOB.dbcon.NewQuery(query_str)
query_books.Execute(query_data)
while (query_books.NextRow())
CHECK_TICK
var/author = query_books.item[1]
var/title = query_books.item[2]
var/content = query_books.item[3]
var/obj/item/book/B = new(src)
B.name = "Book: [title]"
B.title = title
B.author = author
B.dat = content
var/randbook = "book[rand(1,16)]"
B.icon_state = randbook
B.item_state = randbook
update_icon()
/obj/structure/bookcase/libraryspawn/fiction
name = "bookcase" //Because SDMM doesn't recognise the name otherwise, for some reason
spawn_category = "Fiction"
/obj/structure/bookcase/libraryspawn/nonfiction
name = "bookcase" //Because SDMM doesn't recognise the name otherwise, for some reason
spawn_category = "Non-Fiction"
/obj/structure/bookcase/libraryspawn/reference
name = "bookcase" //Because SDMM doesn't recognise the name otherwise, for some reason
spawn_category = "Reference"
/obj/structure/bookcase/libraryspawn/religion
name = "bookcase" //Because SDMM doesn't recognise the name otherwise, for some reason
spawn_category = "Religion"
/obj/structure/bookcase/manuals/medical
name = "Medical Manuals bookcase"
/obj/structure/bookcase/manuals/medical/New()
..()
new /obj/item/book/manual/medical_diagnostics_manual(src)
new /obj/item/book/manual/medical_diagnostics_manual(src)
new /obj/item/book/manual/medical_diagnostics_manual(src)
new /obj/item/book/manual/medical_diagnostics_manual(src)
update_icon()
/obj/structure/bookcase/manuals/engineering
name = "Engineering Manuals bookcase"
/obj/structure/bookcase/manuals/engineering/New()
..()
new /obj/item/book/manual/wiki/engineering_construction(src)
new /obj/item/book/manual/engineering_particle_accelerator(src)
new /obj/item/book/manual/wiki/engineering_hacking(src)
new /obj/item/book/manual/wiki/engineering_guide(src)
new /obj/item/book/manual/atmospipes(src)
new /obj/item/book/manual/engineering_singularity_safety(src)
new /obj/item/book/manual/evaguide(src)
update_icon()
/obj/structure/bookcase/manuals/research_and_development
name = "R&D Manuals bookcase"
/obj/structure/bookcase/manuals/research_and_development/New()
..()
new /obj/item/book/manual/research_and_development(src)
update_icon()
/*
* Barcode Scanner mode defines — used in book/attackby and barcodescanner/attack_self
*/
#define BARCODE_SCAN_BUFFER_ONLY 0
#define BARCODE_SCAN_BUFFER_COMPUTER 1
#define BARCODE_SCAN_CHECKIN 2
#define BARCODE_SCAN_ADD_INVENTORY 3
/*
* Book
*/
/obj/item/book
name = "book"
icon = 'icons/obj/library.dmi'
contained_sprite = TRUE
icon_state = "book"
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
attack_verb = list("bashed", "whacked", "educated")
var/dat // Actual page content
var/due_date = 0 // Game time in 1/10th seconds
var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
var/title // The real name of the book.
var/carved = 0 // Has the book been hollowed out for use as a secret storage item?
var/obj/item/store //What's in the book?
drop_sound = 'sound/items/drop/book.ogg'
pickup_sound = 'sound/items/pickup/book.ogg'
/obj/item/book/antagonist_hints(mob/user, distance, is_adjacent)
. += ..()
. += "As a Cultist, this item can be reforged to become a cult tome."
/obj/item/book/attack_self(var/mob/user as mob)
if(carved)
if(store)
to_chat(user, SPAN_NOTICE("[store] falls out of [title]!"))
store.forceMove(get_turf(src.loc))
store = null
return
else
to_chat(user, SPAN_NOTICE("The pages of [title] have been cut out!"))
return
if(src.dat)
user << browse(HTML_SKELETON("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]"), "window=book")
user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.")
playsound(loc, 'sound/items/bureaucracy/bookopen.ogg', 50, TRUE)
onclose(user, "book")
else
to_chat(user, "This book is completely blank!")
/obj/item/book/attackby(obj/item/attacking_item, mob/user)
if(carved)
if(!store)
if(attacking_item.w_class < 3)
user.drop_from_inventory(attacking_item,src)
store = attacking_item
to_chat(user, SPAN_NOTICE("You put [attacking_item] in [title]."))
return
else
to_chat(user, SPAN_NOTICE("[attacking_item] won't fit in [title]."))
return
else
to_chat(user, SPAN_NOTICE("There's already something in [title]!"))
return
if(attacking_item.tool_behaviour == TOOL_PEN)
if(unique)
to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.")
return
var/choice = tgui_input_list(user, "What would you like to change?", "Book", list("Title", "Contents", "Author", "Cancel"), "Cancel")
switch(choice)
if("Title")
var/newtitle = reject_bad_text(strip_html_full(tgui_input_text(user, "Write a new title:", "Title", encode = FALSE)))
if(!newtitle)
to_chat(usr, "The title is invalid.")
return
else
src.name = html_decode(newtitle)
src.title = newtitle
if("Contents")
var/content = strip_html_readd_newlines(tgui_input_text(user, "Write your book's contents (HTML NOT allowed):", "Content", multiline = TRUE, encode = FALSE), MAX_BOOK_MESSAGE_LEN)
if(!content)
to_chat(usr, "The content is invalid.")
return
else
src.dat += content
if("Author")
var/newauthor = strip_html_full(tgui_input_text(user, "Write the author's name:", "Author", encode = FALSE))
if(!newauthor)
to_chat(usr, "The name is invalid.")
return
else
src.author = newauthor
else
return
else if(istype(attacking_item, /obj/item/barcodescanner))
var/obj/item/barcodescanner/scanner = attacking_item
var/obj/structure/machinery/librarycomp/comp = locate(scanner.computer_ref)
if(!comp || QDELETED(comp))
to_chat(user, "[attacking_item]'s screen flashes: 'No associated computer found!'")
else
switch(scanner.mode)
if(BARCODE_SCAN_BUFFER_ONLY)
scanner.book_ref = REF(src)
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer.'")
if(BARCODE_SCAN_BUFFER_COMPUTER)
scanner.book_ref = REF(src)
comp.buffer_book = src.name
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'")
if(BARCODE_SCAN_CHECKIN)
scanner.book_ref = REF(src)
for(var/datum/borrowbook/checkout in comp.checkouts)
if(checkout.book_name == src.name)
comp.checkouts.Remove(checkout)
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
return
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'")
if(BARCODE_SCAN_ADD_INVENTORY)
scanner.book_ref = REF(src)
var/datum/weakref/src_wref = WEAKREF(src)
if(src_wref in comp.inventory)
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
return
comp.inventory.Add(src_wref)
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
else if(istype(attacking_item, /obj/item/material/knife) || attacking_item.tool_behaviour == TOOL_WIRECUTTER)
if(carved)
return
to_chat(user, SPAN_NOTICE("You begin to carve out [title]."))
if(attacking_item.use_tool(src, user, 30, volume = 50))
to_chat(user, SPAN_NOTICE("You carve out the pages from [title]! You didn't want to read it anyway."))
playsound(loc, 'sound/items/bureaucracy/papercrumple.ogg', 50, 1)
new /obj/item/shreddedp(get_turf(src))
carved = 1
return
else
..()
/obj/item/book/attack(mob/living/target_mob, mob/living/user, target_zone)
if(target_zone == BP_EYES)
user.visible_message(SPAN_NOTICE("You open up the book and show it to [target_mob]. "), \
SPAN_NOTICE(" [user] opens up a book and shows it to [target_mob]. "))
target_mob << browse(HTML_SKELETON("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]"), "window=book")
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam
/*
* Barcode Scanner
*/
/obj/item/barcodescanner
name = "book scanner"
icon = 'icons/obj/library.dmi'
icon_state = "scanner"
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
var/computer_ref // REF string of the associated library computer
var/book_ref // REF string of the currently scanned book
var/mode = BARCODE_SCAN_BUFFER_ONLY
/obj/item/barcodescanner/attack_self(mob/user)
mode += 1
if(mode > BARCODE_SCAN_ADD_INVENTORY)
mode = BARCODE_SCAN_BUFFER_ONLY
to_chat(user, "[src] Status Display:")
var/mode_desc
switch(mode)
if(BARCODE_SCAN_BUFFER_ONLY)
mode_desc = "Scan book to local buffer."
if(BARCODE_SCAN_BUFFER_COMPUTER)
mode_desc = "Scan book to local buffer and set associated computer buffer to match."
if(BARCODE_SCAN_CHECKIN)
mode_desc = "Scan book to local buffer, attempt to check in scanned book."
if(BARCODE_SCAN_ADD_INVENTORY)
mode_desc = "Scan book to local buffer, attempt to add book to general inventory."
else
mode_desc = "ERROR"
to_chat(user, " - Mode [mode] : [mode_desc]")
var/obj/structure/machinery/librarycomp/comp = locate(src.computer_ref)
if(comp && !QDELETED(comp))
to_chat(user, SPAN_NOTICE("Computer has been associated with this unit."))
else
to_chat(user, SPAN_WARNING("No associated computer found. Only local scans will function properly."))
to_chat(user, "\n")
#undef BARCODE_SCAN_BUFFER_ONLY
#undef BARCODE_SCAN_BUFFER_COMPUTER
#undef BARCODE_SCAN_CHECKIN
#undef BARCODE_SCAN_ADD_INVENTORY