diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index d5144e09d0..8d0bb0400c 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -819,7 +819,7 @@ About the new airlock wires panel: usr.machine = src if (href_list["wires"]) var/t1 = text2num(href_list["wires"]) - if (!istype(usr.equipped(), /obj/item/weapon/wirecutters) || !istype(usr.equipped(),/obj/item/weapon/shard)) + if (!(istype(usr.equipped(), /obj/item/weapon/wirecutters) || istype(usr.equipped(),/obj/item/weapon/shard))) usr << "You need wirecutters!" return if (src.isWireColorCut(t1) && istype(usr.equipped(), /obj/item/weapon/wirecutters)) diff --git a/code/game/magic/archived_book.dm b/code/game/magic/archived_book.dm index c5a589816e..8443444aa3 100644 --- a/code/game/magic/archived_book.dm +++ b/code/game/magic/archived_book.dm @@ -1,5 +1,5 @@ #define BOOK_VERSION_MIN 1 -#define BOOK_VERSION_MAX 1 +#define BOOK_VERSION_MAX 2 #define BOOK_PATH "data/books/" var/global/datum/book_manager/book_mgr = new() @@ -67,6 +67,10 @@ datum/archived_book id // the id of the book (like an isbn number) dat // Actual page content + author_real // author's real_name + author_key // author's byond key + list/icon/photos // in-game photos used + // loads the book corresponding by the specified id datum/archived_book/New(var/path) if(isnull(path)) @@ -88,6 +92,12 @@ datum/archived_book/New(var/path) F["id"] >> id F["dat"] >> dat + F["author_real"] >> author_real + F["author_key"] >> author_key + F["photos"] >> photos + if(!photos) + photos = new() + // let's sanitize it here too! for(var/tag in paper_blacklist) if(findtext(dat,"<"+tag)) @@ -105,6 +115,10 @@ datum/archived_book/proc/save() F["id"] << id F["dat"] << dat + F["author_real"] << author_real + F["author_key"] << author_key + F["photos"] << photos + #undef BOOK_VERSION_MIN #undef BOOK_VERSION_MAX #undef BOOK_PATH diff --git a/code/game/magic/library.dm b/code/game/magic/library.dm index aced80ed11..10ac4a1d19 100644 --- a/code/game/magic/library.dm +++ b/code/game/magic/library.dm @@ -73,33 +73,55 @@ var/category New() - var/list/books = book_mgr.getall() - var/list/catbooks = new() - // get books in category - for(var/datum/archived_book/B in books) - if(!category || (category != B.category)) - continue; - catbooks += B - world << "cat [category]: [B.title]" - if(catbooks.len <= 3) - // if 3 or less books, fill shelf with that - for(var/datum/archived_book/AB in catbooks) - var/obj/item/weapon/book/B = new(src) - B.name = "Book: [AB.title]" - B.title = AB.title - B.author = AB.author - B.dat = AB.dat - B.icon_state = "book[rand(1,7)]" - else - // otherwise, pick 3 random books - for(var/i = 1 to 3) - var/datum/archived_book/AB = pick(catbooks) - var/obj/item/weapon/book/B = new(src) - B.name = "Book: [AB.title]" - B.title = AB.title - B.author = AB.author - B.dat = AB.dat - B.icon_state = "book[rand(1,7)]" + spawn(2) // allow library comp to exist + var/list/books = book_mgr.getall() + var/list/catbooks = new() + // see if we have a library computer + var/obj/machinery/librarycomp/comp + if(istype(loc.loc, /area)) + comp = locate() in loc.loc + // get books in category + for(var/datum/archived_book/B in books) + if(!category || (category != B.category)) + continue; + catbooks += B + if(catbooks.len <= 3) + // if 3 or less books, fill shelf with that + for(var/datum/archived_book/AB in catbooks) + var/obj/item/weapon/book/B = new(src) + B.name = "Book: [AB.title]" + B.title = AB.title + B.author = AB.author + B.dat = AB.dat + B.gen_pages() + B.icon_state = "book[rand(1,7)]" + B.ssbn = AB.id + B.author_real = AB.author_real + B.author_key = AB.author_key + B.photos = AB.photos + + // add to inventory + if(comp) + comp.inventory += B + else + // otherwise, pick 3 random books + for(var/i = 1 to 3) + var/datum/archived_book/AB = pick(catbooks) + var/obj/item/weapon/book/B = new(src) + B.name = "Book: [AB.title]" + B.title = AB.title + B.author = AB.author + B.dat = AB.dat + B.gen_pages() + B.icon_state = "book[rand(1,7)]" + B.ssbn = AB.id + B.author_real = AB.author_real + B.author_key = AB.author_key + B.photos = AB.photos + + // add to inventory + if(comp) + comp.inventory += B attackby(obj/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/book)) @@ -201,13 +223,75 @@ unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified title // The real name of the book. - attack_self(var/mob/user as mob) + author_real // author's real_name + author_key // author's byond key + ssbn // the ssbn, if a downloaded book + + list/pages = new() // individual pages as a list of text + cur_page = 1 // current page being read + list/icon/photos // in-game photos used + + proc/navbar() + return "
" \ + + "
"+(cur_page > 1 \ + ? "<" \ + + "<" \ + : "") \ + + "
"+(cur_page < pages.len \ + ? ">" \ + + ">" \ + : "") \ + + "
[cur_page]/[pages.len]
" + + // should be called if dat is changed + proc/gen_pages() + // split into pages + cur_page = 1 + pages = dd_text2list(dat, "") + var/PN = 1 + for(var/page in pages) + // look for photos and process + var/i = 1 + while(i <= lentext(page)) + i = findtext(page, "", i+7) + if(e_c == 0) e_c = INFINITY + var/e = min(e_s, e_c) // find the closest of the two + if(e == INFINITY) break + var/i_num = text2num(copytext(page, i+7, e)) + page = copytext(page, 1, i) + "Penned by [author].
" + "[dat]", "window=book") - if(title) - user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") - else - user.visible_message("[user] opens a book titled \"[src.name]\" and begins reading intently.") + cache_imgs(user) + user << browse("
[title]
[author]
[pages[cur_page]]
[pages.len > 1 ? navbar() : ""]", "window=book;size=600x500") + + if(opening) + if(title) + user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") + else + user.visible_message("[user] opens a book titled \"[src.name]\" and begins reading intently.") + onclose(user, "book") else user << "This book is completely blank!" @@ -220,11 +304,12 @@ var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel") switch(choice) if("Title") - var/title = input("Write a new title:") as text|null - if(!title) + var/ntitle = input("Write a new title:") as text|null + if(!ntitle) return else - src.name = sanitize(title) + title = sanitize(ntitle) + name = "Book: [title]" if("Contents") var/t = "[src.dat]" do @@ -248,6 +333,7 @@ return src.dat = t + gen_pages() if("Author") var/nauthor = input("Write the author's name:") as text|null if(!nauthor) @@ -609,9 +695,11 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f dat += "No scanner found within wireless network range.
" else if(!scanner.cache) dat += "No data found in scanner memory.
" + else if(scanner.cache.ssbn && (scanner.cache.author_real != user.real_name && scanner.cache.author_key != user.client.ckey)) + dat += "This book contains copy protection preventing you from re-uploading this to the database.
" else - dat += "Data marked for upload...
" - dat += "Title: [scanner.cache.name]
" + dat += "Data marked for [scanner.cache.ssbn ? "re-" : ""]upload...
" + dat += "Title: [scanner.cache.title]
" if(!scanner.cache.author) scanner.cache.author = "Anonymous" dat += "Author: [scanner.cache.author]
" @@ -704,8 +792,13 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f if(href_list["delbook"]) var/obj/item/weapon/book/b = locate(href_list["delbook"]) inventory.Remove(b) + if(href_list["settitle"]) + var/newtitle = input("Enter the book name: ", "Book Upload", scanner.cache.title) as text|null + if(newtitle) + scanner.cache.title = sanitize(newtitle) + scanner.cache.name = "Book: [scanner.cache.title]" if(href_list["setauthor"]) - var/newauthor = input("Enter the author's name: ") as text|null + var/newauthor = input("Enter the author's name: ", "Book Upload", scanner.cache.author) as text|null if(newauthor) scanner.cache.author = sanitize(newauthor) if(href_list["setcategory"]) @@ -743,11 +836,22 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f dbcon.Disconnect() else var/datum/archived_book/B = new() - B.title = scanner.cache.name + if(scanner.cache.title) + B.title = scanner.cache.title + else + B.title = scanner.cache.name B.author = scanner.cache.author B.dat = scanner.cache.dat B.category = upload_category - B.id = book_mgr.freeid() + if(scanner.cache.ssbn) + B.id = scanner.cache.ssbn + else + B.id = book_mgr.freeid() + B.author_real = scanner.cache.author_real + B.author_key = scanner.cache.author_key + if(scanner.cache.photos.len >= 8) + scanner.cache.photos.len = 8 + B.photos = scanner.cache.photos B.save() log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") @@ -775,6 +879,7 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f B.title = title B.author = author B.dat = content + B.gen_pages() B.icon_state = "book[rand(1,7)]" src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") break @@ -789,7 +894,11 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f B.title = AB.title B.author = AB.author B.dat = AB.dat + B.gen_pages() B.icon_state = "book[rand(1,7)]" + B.author_real = AB.author_real + B.author_key = AB.author_key + B.photos = AB.photos src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") if(href_list["orderbyid"]) @@ -873,18 +982,236 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f anchored = 1 density = 1 + var/obj/item/weapon/book/template + var/list/icon_names + var/i_preview = 1 + var/printing = 0 + + proc/newGenericBook() + template = new(src) + template.title = "Print Job #" + "[rand(100, 999)]" + template.author = "Anonymous" + template.name = "Book: [template.title]" + template.icon_state = "book[rand(1,7)]" + template.dat = "" + template.gen_pages() + template.photos = new() + icon_names = new() + i_preview = 1 + attackby(var/obj/O as obj, var/mob/user as mob) if(istype(O, /obj/item/weapon/paper)) user.drop_item() O.loc = src user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(200,400)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/weapon/book/b = new(src.loc) - b.dat = O:info - b.name = "Print Job #" + "[rand(100, 999)]" - b.icon_state = "book[rand(1,7)]" + + if(template) + template.dat += "[O:info]" + template.gen_pages() + else + template = new(src) + template.title = O.name == initial(O.name) ? "Print Job #" + "[rand(100, 999)]" : O.name + template.name = "Book: [template.title]" + template.icon_state = "book[rand(1,7)]" + template.dat = O:info + template.gen_pages() + template.photos = new() + icon_names = new() + i_preview = 1 del(O) + updateUsrDialog() + else if(istype(O, /obj/item/weapon/photo)) + user.drop_item() + O.loc = src + user.visible_message("[user] loads a photo into [src].", "You load a photo into [src].") + + if(!template) + newGenericBook() + if(template.photos.len >= 8) + user << "\red The photo tray is already full!" + O.loc = src.loc + return + + var/icon/imported = new(O.icon) + imported.Crop(6,8,27,27) + imported.Scale(32,32) + template.photos += imported + icon_names += O.name + del(O) + updateUsrDialog() + else if(istype(O, /obj/item/weapon/book)) + if(template) + user << "\red The binder already has a book in the buffer!" + return + + user.drop_item() + O.loc = src + user.visible_message("[user] loads a book into [src] for editing.", "You load a book into [src] for editing.") + + var/obj/machinery/librarycomp/C = locate() in loc.loc + if(C) + if(C.inventory.Find(O)) + C.inventory -= O + template = O + icon_names = new() + for(var/i = 1 to template.photos.len) + icon_names += "Photo [i]" + updateUsrDialog() else - ..() \ No newline at end of file + ..() + +/obj/machinery/bookbinder/attack_paw(user as mob) + return src.attack_hand(user) + +/obj/machinery/bookbinder/attack_ai(user as mob) + return src.attack_hand(user) + +/obj/machinery/bookbinder/attack_hand(user as mob) + if(..()) + return + + if(src.stat) + user << "[name] does not seem to be responding to your button mashing." + return + + var/dat = "Book BinderNT Publishing House
" + + if(printing) + dat += "[src] is currently printing." + else + // general settings + dat += "New Book" + + if(template) + template.cache_imgs(user) + + dat += " Delete Book
" + + dat += "Title: [template.title]
" + dat += "Author: [template.author]
" + + // articles + dat += "Pages:
    " + var/ID = 1 + for(var/P in template.pages) + dat += "
  1. Page [ID]" + dat += " Preview" + dat += " Delete
  2. " + ID++ + + dat += "
New Page
" + + // images + dat += "Images:
    " + ID = 1 + for(var/icon/I in template.photos) + dat += "
  1. [ID]: [icon_names[ID]]" + dat += " Delete" + ID++ + dat += "
" + if(i_preview >= 1 && i_preview <= template.photos.len) + var/iconname = "book_preview.png" + user << browse_rsc(template.photos[i_preview],iconname) + dat += "[i_preview] ([icon_names[i_preview]]): " + dat += "
Print" + + dat += "
" + user << browse(dat, "window=bookbinder") + onclose(user, "bookbinder") + + +/obj/machinery/bookbinder/Topic(href, href_list) + if(..()) + return + usr.machine = src + src.add_fingerprint(usr) + + if(href_list["new"]) + if(template) + var/R = alert("There is already a book in the buffer. Do you want to delete it and start over?", "Book Binder", "Delete", "Cancel") + if(R == "Cancel") + return + del(template) + newGenericBook() + updateUsrDialog() + if(href_list["delete"]) + if(template) + del(template) + updateUsrDialog() + if(href_list["print"]) + printing = 1 + updateUsrDialog() + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(50,100)) + src.visible_message("[src] whirs as it prints and binds a new book.") + + if(!template.ssbn) + template.author_real = usr.real_name + if(usr.client) + template.author_key = usr.client.ckey + template.loc = src.loc + template = null + printing = 0 + updateUsrDialog() + if(href_list["p_new"]) + template.dat += "" + template.gen_pages() + updateUsrDialog() + + if(href_list["title"]) + var/n_name = input(usr, "What would you like to title your book?", "Book Binder", template.title) as text|null + if(n_name) + template.title = sanitize(n_name) + template.name = "Book: [template.title]" + updateUsrDialog() + if(href_list["author"]) + var/n_name = input(usr, "Who would you like your pen name to be?", "Book Binder", template.author) as text|null + if(n_name) + template.author = sanitize(n_name) + updateUsrDialog() + + if(href_list["p_edit"]) + var/PN = text2num(href_list["p_edit"]) + var/list/pages = dd_text2list(template.dat, "") + var/t = pages[PN] + do + t = input(usr, "What text do you wish to add?", "Book Binder P.[PN]", t) as message + + if(lentext(t) >= MAX_BOOK_MESSAGE_LEN) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(lentext(t) > MAX_BOOK_MESSAGE_LEN) + + // check for exploits + for(var/tag in paper_blacklist) + if(findtext(t,"<"+tag)) + usr << "\blue You think to yourself, \"Hm.. this is only paper...\"" + return + + // the actual pages list is formatted and shouldn't be directly editted + pages[PN] = t + template.dat = dd_list2text(pages, "") + template.gen_pages() + updateUsrDialog() + if(href_list["p_delete"]) + var/i = text2num(href_list["p_delete"]) + var/list/pages = dd_text2list(template.dat, "") + pages.Cut(i, i+1) + template.dat = dd_list2text(pages, "") + template.gen_pages() + updateUsrDialog() + if(href_list["p_preview"]) + var/i = text2num(href_list["p_preview"]) + var/dat = template.pages[i] + usr << browse(dat, "window=bookbinder_preview;size=600x500") + onclose(usr, "bookbinder_preview") + + if(href_list["i_view"]) + i_preview = text2num(href_list["i_view"]) + updateUsrDialog() + if(href_list["i_delete"]) + var/i = text2num(href_list["i_delete"]) + template.photos.Cut(i, i+1) + icon_names.Cut(i, i+1) + updateUsrDialog() diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index ecb0b24d9a..9df99a5817 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -119,7 +119,7 @@ RCD desc = "A RCD. It currently holds [matter]/[max_matter] matter-units." return else if(mode == 3 && (istype(A, /turf) || istype(A, /obj/machinery/door/airlock) ) ) - if(istype(A, /turf/simulated/wall) && matter >= 4) + if(istype(A, /turf/simulated/wall) && !istype(A, /turf/simulated/wall/r_wall) && matter >= 4) user << "Deconstructing Wall (4)..." playsound(src.loc, 'click.ogg', 50, 1) if(do_after(user, 40)) diff --git a/code/game/vote.dm b/code/game/vote.dm index 7741f2c182..cc75bde849 100644 --- a/code/game/vote.dm +++ b/code/game/vote.dm @@ -362,6 +362,7 @@ return //world << "You're voting for [N] options!" var/i + vote.choices = list() for(i=1; i<=N; i++) var/addvote = input(usr, "What is option #[i]?", "Enter Option #[i]") as text vote.choices += addvote diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 8fa08fea4e..fac90da513 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -2240,6 +2240,7 @@ return var/i + vote.choices = list() for(i=1; i<=N; i++) var/addvote = input(usr, "What is option #[i]?", "Enter Option #[i]") as text vote.choices += addvote diff --git a/code/modules/chemical/Chemistry-Reagents.dm b/code/modules/chemical/Chemistry-Reagents.dm index 52062de5e8..8bafb59cc9 100644 --- a/code/modules/chemical/Chemistry-Reagents.dm +++ b/code/modules/chemical/Chemistry-Reagents.dm @@ -1822,7 +1822,7 @@ datum coco name = "Coco Powder" - id = "Coco Powder" + id = "coco" description = "A fatty, bitter paste made from coco beans." reagent_state = SOLID nutriment_factor = 5 * REAGENTS_METABOLISM diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index d05272e903..11bcdc5bb1 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -873,8 +873,8 @@ /obj/machinery/power/apc/proc/malfoccupy(var/mob/living/silicon/ai/malf) if(!istype(malf)) return - if(src.z != 1) - return + if(src.z != 1) + return src.occupant = new /mob/living/silicon/ai(src,malf.laws,null,1) src.occupant.adjustOxyLoss(malf.getOxyLoss())