Files
VOREStation/code/modules/library/lib_items.dm
Cameron Lennox d5849910e5 Begin clickcode attack_self fix (#18797)
* Begin clickcode attack_self fix

Begins the work to make everything call back to parent for attack_self so that signals are sacred.

* Makes MORE things call the attack_self() parent

Yes, I could make special_handling a var on obj/item HOWEVER i want it to be specific so it can be tracked down later and ONLY the objects that use it can be refactored instead of sitting there literally forever and it just becoming 'a thing'.

* Finishes making the rest of attack_self call parent.

As mentioned, things such as 'specialty_goggles' 'special_handling' and the such are only there to help with attack_self until the attack_self is recoded for those items.

* begone foul demon

* some more cleanup

* These

* GOD this was annoying

* yeh

* Fix this

* fLARES

* Thesee too

* toys!

* Even more!

* More fixes

* Even more

* rest of em

* these too

* Update syndie.dm

* hardref clear

* Update code/game/gamemodes/nuclear/pinpointer.dm

* Update code/game/objects/effects/mines.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/contraband_vr.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/weapons/gift_wrappaper.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* these too

* Update maintpanel_stack.dm

* angry warning

* Fixes packaged snacks.

Fixes improper var default.

* Special handling for these

* proper poly types

* Fixes magclaws

Makes the 'features' it had just part  of base magboots that can be adjusted via varswap.

* Fixes jackets

Fixes https://github.com/VOREStation/VOREStation/issues/18941

* Small bugfix

Makes p_Theyre properly capitialize
Makes examine show proper wording

* Update gift_wrappaper.dm
2025-12-29 13:21:10 -05:00

430 lines
14 KiB
Plaintext

/* Library Items
*
* Contains:
* Bookcase
* Book
* Barcode Scanner
*/
/*
* Bookcase
*/
/obj/structure/bookcase
name = "bookcase"
desc = "A set of wooden shelves, perfect for placing books on."
icon = 'icons/obj/library.dmi'
icon_state = "book-0"
anchored = TRUE
density = TRUE
opacity = 1
/obj/structure/bookcase/Initialize(mapload)
. = ..()
for(var/obj/item/I in loc)
if(istype(I, /obj/item/book))
I.loc = src
update_icon()
AddElement(/datum/element/climbable)
/obj/structure/bookcase/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/book))
user.drop_item()
O.loc = src
update_icon()
else if(istype(O, /obj/item/pen))
var/newname = sanitizeSafe(tgui_input_text(user, "What would you like to title this bookshelf?", null, null, MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
if(!newname)
return
else
name = ("bookcase ([newname])")
else if(O.has_tool_quality(TOOL_WRENCH))
playsound(src, O.usesound, 100, 1)
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(O.has_tool_quality(TOOL_SCREWDRIVER))
playsound(src, O.usesound, 75, 1)
to_chat(user, span_notice("You begin dismantling \the [src]."))
if(do_after(user, 25 * O.toolspeed, target = src))
to_chat(user, span_notice("You dismantle \the [src]."))
new /obj/item/stack/material/wood(get_turf(src), 3)
for(var/obj/item/book/b in contents)
b.loc = (get_turf(src))
qdel(src)
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?", "Book Selection", contents)
if(choice)
if(!user.canmove || user.stat || user.restrained() || !in_range(loc, user))
return
if(ishuman(user))
if(!user.get_active_hand())
user.put_in_hands(choice)
else
choice.loc = 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.loc = (get_turf(src))
else qdel(b)
qdel(src)
return
if(3.0)
if (prob(50))
for(var/obj/item/book/b in contents)
b.loc = (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"
/*
Book Cart
*/
/obj/structure/bookcase/bookcart
name = "book cart"
icon = 'icons/obj/library.dmi'
icon_state = "bookcart-0"
anchored = FALSE
opacity = 0
/obj/structure/bookcase/bookcart/attackby(obj/item/O as obj, mob/user)
if(istype(O, /obj/item/book))
user.drop_item()
O.loc = src
update_icon()
else
return
/obj/structure/bookcase/bookcart/update_icon()
if(contents.len < 5)
icon_state = "bookcart-[contents.len]"
else
icon_state = "bookcart-5"
/*
Book Cart End
*/
/obj/structure/bookcase/manuals/medical
name = "Medical Manuals bookcase"
/obj/structure/bookcase/manuals/medical/Initialize(mapload)
new /obj/item/book/manual/medical_cloning(src)
new /obj/item/book/manual/wiki/medical_diagnostics_manual(src)
new /obj/item/book/manual/wiki/medical_diagnostics_manual(src)
new /obj/item/book/manual/wiki/medical_diagnostics_manual(src)
. = ..()
/obj/structure/bookcase/manuals/engineering
name = "Engineering Manuals bookcase"
/obj/structure/bookcase/manuals/engineering/Initialize(mapload)
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)
. = ..()
/obj/structure/bookcase/manuals/research_and_development
name = "R&D Manuals bookcase"
/obj/structure/bookcase/manuals/research_and_development/Initialize(mapload)
new /obj/item/book/manual/research_and_development(src)
. = ..()
/*
* Book
*/
/obj/item/book
name = "book"
icon = 'icons/obj/library.dmi'
icon_state ="book"
item_icons = list(
slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi',
slot_r_hand_str = 'icons/mob/items/righthand_books.dmi'
)
item_state = "book"
throw_speed = 1
throw_range = 5
flags = NOCONDUCT
w_class = ITEMSIZE_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/libcategory = "Miscellaneous" // The library category this book sits in. "Fiction", "Non-Fiction", "Adult", "Reference", "Religion"
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?
var/occult_tier = 0 //If the book is an occult book or not and how strong it is. Used for attack_self
///Var for attack_self chain
var/special_handling = FALSE
drop_sound = 'sound/items/drop/book.ogg'
pickup_sound = 'sound/items/pickup/book.ogg'
/obj/item/book/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
if(occult_tier)
return FALSE
if(special_handling)
return FALSE
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(dat)
display_content(user)
user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.")
playsound(src, 'sound/bureaucracy/bookopen.ogg', 50, 1)
onclose(user, "book")
playsound(src, 'sound/bureaucracy/bookclose.ogg', 50, 1)
else
to_chat(user, "This book is completely blank!")
/obj/item/book/proc/display_content(mob/living/user)
if(!findtext(dat, regex("^<html")))
dat = "<html>[dat]</html>"
user << browse(replacetext(dat, "<html>", "<html><TT><I>Penned by [author].</I></TT> <BR>"), "window=book")
/obj/item/book/attackby(obj/item/W, mob/user)
if(carved)
if(!store)
if(W.w_class < ITEMSIZE_LARGE)
user.drop_item()
W.loc = src
store = W
to_chat(user, span_notice("You put [W] in [title]."))
return
else
to_chat(user, span_notice("[W] won't fit in [title]."))
return
else
to_chat(user, span_notice("There's already something in [title]!"))
return
if(istype(W, /obj/item/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?", "Change What?", list("Title", "Contents", "Author", "Cancel"))
switch(choice)
if("Title")
var/newtitle = reject_bad_text(sanitizeSafe(tgui_input_text(user, "Write a new title:", encode = FALSE)))
if(!newtitle)
to_chat(user, "The title is invalid.")
return
else
src.name = newtitle
src.title = newtitle
if("Contents")
var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed):", max_length=MAX_BOOK_MESSAGE_LEN, multiline=TRUE)
if(!content)
to_chat(user, "The content is invalid.")
return
else
src.dat += content
if("Author")
var/newauthor = tgui_input_text(user, "Write the author's name:", "", "", MAX_LNAME_LEN)
if(!newauthor)
to_chat(user, "The name is invalid.")
return
else
src.author = newauthor
else
return
else if(istype(W, /obj/item/barcodescanner))
var/obj/item/barcodescanner/scanner = W
if(!scanner.computer)
to_chat(user, "[W]'s screen flashes: 'No associated computer found!'")
else
switch(scanner.mode)
if(0)
scanner.book = src
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'")
if(1)
scanner.book = src
scanner.computer.buffer_book = src.name
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'")
if(2)
scanner.book = src
for(var/datum/borrowbook/b in scanner.computer.checkouts)
if(b.bookname == src.name)
scanner.computer.checkouts.Remove(b)
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
return
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'")
if(3)
scanner.book = src
for(var/obj/item/book in scanner.computer.inventory)
if(book == src)
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
return
scanner.computer.inventory.Add(src)
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
else if(istype(W, /obj/item/material/knife) || W.has_tool_quality(TOOL_WIRECUTTER))
if(carved) return
to_chat(user, span_notice("You begin to carve out [title]."))
if(do_after(user, 3 SECONDS, target = src))
to_chat(user, span_notice("You carve out the pages from [title]! You didn't want to read it anyway."))
playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1)
new /obj/item/shreddedp(get_turf(src))
carved = 1
return
else
..()
/obj/item/book/attack(mob/living/carbon/M, mob/living/carbon/user)
if(user.zone_sel.selecting == O_EYES)
user.visible_message(span_notice("You open up the book and show it to [M]."), \
span_notice(" [user] opens up a book and shows it to [M]."))
display_content(M)
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam
/*
* Book Bundle (Multi-page book)
*/
/obj/item/book/bundle
var/page = 1 //current page
var/list/pages = list() //the contents of each page
special_handling = TRUE
/obj/item/book/bundle/proc/show_content(mob/user)
if(!pages.len)
return
var/dat
var/obj/item/W = pages[page]
// first
if(page == 1)
dat+= "<DIV STYLE='float:left; text-align:left; width:33.33333%'><A href='byond://?src=\ref[src];prev_page=1'>Front</A></DIV>"
dat+= "<DIV STYLE='float:right; text-align:right; width:33.33333%'><A href='byond://?src=\ref[src];next_page=1'>Next Page</A></DIV><BR><HR>"
// last
else if(page == pages.len)
dat+= "<DIV STYLE='float:left; text-align:left; width:33.33333%'><A href='byond://?src=\ref[src];prev_page=1'>Previous Page</A></DIV>"
dat+= "<DIV STYLE='float:right; text-align:right; with:33.33333%'><A href='byond://?src=\ref[src];next_page=1'>Back</A></DIV><BR><HR>"
// middle pages
else
dat+= "<DIV STYLE='float:left; text-align:left; width:33.33333%'><A href='byond://?src=\ref[src];prev_page=1'>Previous Page</A></DIV>"
dat+= "<DIV STYLE='float:right; text-align:right; width:33.33333%'><A href='byond://?src=\ref[src];next_page=1'>Next Page</A></DIV><BR><HR>"
if(istype(pages[page], /obj/item/paper))
var/obj/item/paper/P = W
if(!(ishuman(user) || isobserver(user) || issilicon(user)))
dat += "<HTML><HEAD><TITLE>[P.name]</TITLE></HEAD><BODY>[stars(P.info)][P.stamps]</BODY></HTML>"
else
dat += "<HTML><HEAD><TITLE>[P.name]</TITLE></HEAD><BODY>[P.info][P.stamps]</BODY></HTML>"
user << browse(dat, "window=[name]")
else if(istype(pages[page], /obj/item/photo))
var/obj/item/photo/P = W
user << browse_rsc(P.img, "tmp_photo.png")
user << browse("<html>" + dat + "<head><title>[P.name]</title></head>" \
+ "<body style='overflow:hidden'>" \
+ "<div> <img src='tmp_photo.png' width = '180'" \
+ "[P.scribble ? "<div> Written on the back:<br><i>[P.scribble]</i>" : null]"\
+ "</body></html>", "window=[name]")
else if(!isnull(pages[page]))
if(!(ishuman(user) || isobserver(user) || issilicon(user)))
dat += "<HTML><HEAD><TITLE>Page [page]</TITLE></HEAD><BODY>[stars(pages[page])]</BODY></HTML>"
else
dat += "<HTML><HEAD><TITLE>Page [page]</TITLE></HEAD><BODY>[pages[page]]</BODY></HTML>"
user << browse(dat, "window=[name]")
/obj/item/book/bundle/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
src.show_content(user)
add_fingerprint(user)
update_icon()
return
/obj/item/book/bundle/Topic(href, href_list)
if(..())
return 1
if((src in usr.contents) || (istype(src.loc, /obj/item/folder) && (src.loc in usr.contents)))
usr.set_machine(src)
if(href_list["next_page"])
if(page != pages.len)
page++
playsound(src, "pageturn", 50, 1)
if(href_list["prev_page"])
if(page > 1)
page--
playsound(src, "pageturn", 50, 1)
src.attack_self(usr)
updateUsrDialog(usr)
else
to_chat(usr, span_notice("You need to hold it in your hands!"))
/*
* Barcode Scanner
*/
/obj/item/barcodescanner
name = "barcode scanner"
icon = 'icons/obj/library.dmi'
icon_state ="scanner"
throw_speed = 1
throw_range = 5
w_class = ITEMSIZE_SMALL
var/obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this
var/obj/item/book/book // Currently scanned book
var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory
/obj/item/barcodescanner/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
mode += 1
if(mode > 3)
mode = 0
to_chat(user, "[src] Status Display:")
var/modedesc
switch(mode)
if(0)
modedesc = "Scan book to local buffer."
if(1)
modedesc = "Scan book to local buffer and set associated computer buffer to match."
if(2)
modedesc = "Scan book to local buffer, attempt to check in scanned book."
if(3)
modedesc = "Scan book to local buffer, attempt to add book to general inventory."
else
modedesc = "ERROR"
to_chat(user, " - Mode [mode] : [modedesc]")
if(src.computer)
to_chat(user, span_green("Computer has been associated with this unit."))
else
to_chat(user, span_red("No associated computer found. Only local scans will function properly."))
to_chat(user, "\n")