diff --git a/baystation12.dme b/baystation12.dme index 40777b76759..13f0147d9f7 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -503,6 +503,7 @@ #include "code\game\machinery\kitchen\processor.dm" #include "code\game\machinery\pipe\construction.dm" #include "code\game\machinery\pipe\pipe_dispenser.dm" +#include "code\game\magic\archived_book.dm" #include "code\game\magic\library.dm" #include "code\game\magic\musician.dm" #include "code\game\magic\cultist\ritual.dm" diff --git a/code/game/magic/archived_book.dm b/code/game/magic/archived_book.dm new file mode 100644 index 00000000000..0e9225c66b6 --- /dev/null +++ b/code/game/magic/archived_book.dm @@ -0,0 +1,77 @@ +#define BOOK_VERSION_MIN 1 +#define BOOK_VERSION_MAX 1 +#define BOOK_PATH "data/books/" + +var/global/datum/book_manager/book_mgr = new() + +datum/book_manager/proc/path(id) + if(isnum(id)) // kill any path exploits + return "[BOOK_PATH][id].sav" + +datum/book_manager/proc/getall() + var/list/paths = flist(BOOK_PATH) + var/list/books = new() + + for(var/path in paths) + var/datum/archived_book/B = new(BOOK_PATH + path) + books += B + + return books + +datum/book_manager/proc/freeid() + var/list/paths = flist(BOOK_PATH) + var/id = paths.len + 101 + + // start at 101+number of books, which will be correct id if none have been deleted, etc + // otherwise, keep moving forward until we find an open id + while(fexists(path(id))) + id++ + + return id + +// delete a book +datum/book_manager/proc/remove(var/id) + fdel(path(id)) + +datum/archived_book + var + author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title // The real name of the book. + category // The category/genre of the book + id // the id of the book (like an isbn number) + dat // Actual page content + +// loads the book corresponding by the specified id +datum/archived_book/New(var/path) + if(isnull(path)) + return + + var/savefile/F = new(path) + + var/version + F["version"] >> version + + if (isnull(version) || version < BOOK_VERSION_MIN || version > BOOK_VERSION_MAX) + fdel(path) + usr << "What book?" + return 0 + + F["author"] >> author + F["title"] >> title + F["category"] >> category + F["id"] >> id + F["dat"] >> dat + +datum/archived_book/proc/save() + var/savefile/F = new(book_mgr.path(id)) + + F["version"] << BOOK_VERSION_MAX + F["author"] << author + F["title"] << title + F["category"] << category + F["id"] << id + F["dat"] << dat + +#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 8f65ab1b7ab..ca6e10cfd9c 100644 --- a/code/game/magic/library.dm +++ b/code/game/magic/library.dm @@ -70,6 +70,36 @@ anchored = 1 density = 1 opacity = 1 + 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 && !findtext(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)]" attackby(obj/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/book)) @@ -192,13 +222,29 @@ return else src.name = sanitize(title) - else if("Contents") - var/content = strip_html(input("Write your book's contents (HTML NOT allowed):"),8192) as message|null - if(!content) + if("Contents") + var/t = "[src.dat]" + do + t = input(user, "What text do you wish to add?", src.name, t) as message + if ((!in_range(src, usr) && src.loc != user && src.loc.loc != user && user.equipped() != W)) + return + + if(lentext(t) >= MAX_PAPER_MESSAGE_LEN) + var/cont = input(user, "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_PAPER_MESSAGE_LEN) + + if ((!in_range(src, usr) && src.loc != user && src.loc.loc != user && user.equipped() != W)) return - else - src.dat += content - else if("Author") + + // check for exploits + if(findtext(t, "There is some old writing on this floor. You are barely able to read out a few lines from a tangled scribble.

Bruce Stachie the First

"; icon_state = "L14"},/area/hallway/primary/central) "aGn" = (/turf/simulated/floor{icon_state = "L16"},/area/hallway/primary/central) -"aGo" = (/obj/structure/bookcase{name = "bookcase (Adult)"},/turf/simulated/floor{icon_state = "wood"},/area/library) +"aGo" = (/obj/structure/bookcase{category = "Adult"; name = "bookcase (Adult)"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aGp" = (/obj/machinery/alarm{dir = 8; icon_state = "alarm0"; pixel_x = 24},/turf/simulated/floor{icon_state = "wood"},/area/library) "aGq" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/obj/structure/stool{pixel_y = 8},/obj/effect/landmark{name = "blobstart"},/turf/simulated/floor{icon_state = "dark"},/area/library) "aGr" = (/obj/machinery/atmospherics/pipe/simple{dir = 9; icon_state = "intact-f"},/obj/structure/bookcase{name = "Forbidden Knowledge"},/turf/simulated/floor{icon_state = "dark"},/area/library) @@ -1795,7 +1795,7 @@ "aIA" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/plating,/area/hallway/primary/central) "aIB" = (/turf/simulated/floor{dir = 1; icon_state = "bluecorner"},/area/hallway/primary/central) "aIC" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/plating,/area/library) -"aID" = (/obj/structure/bookcase{name = "bookcase (Religious)"},/turf/simulated/floor{icon_state = "wood"},/area/library) +"aID" = (/obj/structure/bookcase{category = "Religion"; name = "bookcase (Religious)"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aIE" = (/obj/item/device/radio/intercom{dir = 1; name = "Station Intercom (General)"; pixel_y = 20},/obj/structure/table/woodentable,/obj/item/weapon/paper,/turf/simulated/floor{icon_state = "wood"},/area/library) "aIF" = (/obj/structure/table/woodentable,/obj/machinery/librarycomp{pixel_y = 8},/turf/simulated/floor{icon_state = "wood"},/area/library) "aIG" = (/obj/structure/stool{pixel_y = 8},/turf/simulated/floor{icon_state = "wood"},/area/library) @@ -1932,7 +1932,7 @@ "aLh" = (/turf/simulated/floor{icon_state = "whitehall"; dir = 1},/area/bridge) "aLi" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0; tag = "Streight"},/obj/structure/stool/chair{dir = 1},/turf/simulated/floor{icon_state = "whitehall"; dir = 1},/area/bridge) "aLj" = (/turf/simulated/floor{icon_state = "whitehall"; dir = 5},/area/bridge) -"aLk" = (/obj/structure/bookcase{name = "bookcase (Fiction)"},/turf/simulated/floor{icon_state = "wood"},/area/library) +"aLk" = (/obj/structure/bookcase{category = "Fiction"; name = "bookcase (Fiction)"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aLl" = (/obj/structure/table/woodentable,/obj/item/weapon/pen,/turf/simulated/floor{icon_state = "wood"},/area/library) "aLm" = (/obj/machinery/door/window/northright{dir = 1; name = "library desk door"; req_access_txt = "37"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aLn" = (/obj/machinery/camera{c_tag = "Hydroponics West Maintenance"; dir = 8; network = "SS13"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0; tag = "Streight"},/obj/machinery/atmospherics/pipe/manifold{color = "red"; dir = 8; icon_state = "manifold-r-f"; initialize_directions = 11; level = 1; name = "pipe manifold"},/turf/simulated/floor/plating,/area/maintenance/fsmaint) @@ -2068,7 +2068,7 @@ "aNN" = (/obj/machinery/door/firedoor/border_only{dir = 8},/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/turf/simulated/floor{dir = 9; icon_state = "blue"},/area/hallway/primary/central) "aNO" = (/obj/item/device/radio/intercom{broadcasting = 0; layer = 4; name = "Station Intercom (General)"; pixel_y = 20},/turf/simulated/floor{dir = 1; icon_state = "blue"},/area/hallway/primary/central) "aNP" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor,/area/hallway/primary/central) -"aNQ" = (/obj/structure/bookcase{name = "bookcase (Non-Fiction)"},/turf/simulated/floor{icon_state = "wood"},/area/library) +"aNQ" = (/obj/structure/bookcase{category = "Non-Fiction"; name = "bookcase (Non-Fiction)"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aNR" = (/obj/structure/stool/chair,/turf/simulated/floor,/area/library) "aNS" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0; tag = "Streight"},/turf/simulated/floor{icon_state = "dark"},/area/library) "aNT" = (/obj/machinery/hydroponics,/obj/structure/disposalpipe/segment,/turf/simulated/floor{icon_state = "dark"},/area/hydroponics) @@ -2224,7 +2224,7 @@ "aQN" = (/obj/machinery/camera{c_tag = "Bridge East"; dir = 1},/obj/structure/closet/emcloset,/turf/simulated/floor{dir = 0; icon_state = "blue"},/area/bridge) "aQO" = (/obj/machinery/door/firedoor/border_only{dir = 8},/turf/simulated/floor{icon_state = "blue"; dir = 10},/area/hallway/primary/central) "aQP" = (/obj/machinery/power/apc{dir = 2; name = "Central Hall APC"; pixel_y = -24},/obj/structure/cable,/turf/simulated/floor{dir = 0; icon_state = "blue"},/area/hallway/primary/central) -"aQQ" = (/obj/structure/bookcase{name = "bookcase (Reference)"},/turf/simulated/floor{icon_state = "wood"},/area/library) +"aQQ" = (/obj/structure/bookcase{category = "Reference"; name = "bookcase (Reference)"},/turf/simulated/floor{icon_state = "wood"},/area/library) "aQR" = (/obj/structure/stool/chair{dir = 1},/turf/simulated/floor,/area/library) "aQS" = (/obj/machinery/atmospherics/pipe/simple{color = "red"; dir = 5; icon_state = "intact-r-f"; level = 1; name = "pipe"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0; tag = "Streight"},/turf/simulated/floor/plating,/area/maintenance/fsmaint) "aQT" = (/obj/machinery/hydroponics,/obj/machinery/atmospherics/pipe/manifold{color = "red"; dir = 1; icon_state = "manifold-r-f"; level = 1; name = "pipe manifold"},/turf/simulated/floor{icon_state = "dark"},/area/hydroponics) @@ -6339,7 +6339,7 @@ "crU" = (/obj/structure/signpost,/turf/unsimulated/floor{icon_state = "asteroid"; name = "dust"},/area) "crV" = (/obj/structure/bookcase/manuals/medical,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "crW" = (/obj/effect/decal/cleanable/cobweb,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) -"crX" = (/obj/structure/bookcase{name = "bookcase (Adult)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) +"crX" = (/obj/structure/bookcase{category = "Adult"; name = "bookcase (Adult)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "crY" = (/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "crZ" = (/obj/structure/bookcase{name = "bookcase (Reports)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csa" = (/obj/structure/table/woodentable,/obj/machinery/librarycomp,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) @@ -6351,7 +6351,7 @@ "csg" = (/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) "csh" = (/obj/structure/table/woodentable,/obj/effect/landmark{name = "Teleport-Scroll"},/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) "csi" = (/obj/structure/bookcase/manuals/engineering,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) -"csj" = (/obj/structure/bookcase{name = "bookcase (Fiction)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) +"csj" = (/obj/structure/bookcase{category = "Fiction"; name = "bookcase (Fiction)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csk" = (/obj/structure/stool,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csl" = (/obj/machinery/door/window/eastleft,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csm" = (/obj/structure/stool/bed,/obj/item/weapon/bedsheet,/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) @@ -6360,7 +6360,7 @@ "csp" = (/obj/structure/bookcase/manuals/research_and_development,/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csq" = (/obj/structure/bookcase{name = "bookcase (Tactics)"},/turf/unsimulated/floor{dir = 8; icon_state = "wood"},/area/wizard_station) "csr" = (/obj/structure/table/woodentable,/obj/item/weapon/paper{info = "

LIST OF SPELLS AVAILABLE

Magic Missile:
This spell fires several, slow moving, magic projectiles at nearby targets. If they hit a target, it is paralyzed and takes minor damage.

Fireball:
This spell fires a fireball at a target and does not require wizard garb. Be careful not to fire it at people that are standing next to you.

Disintegrate:
This spell instantly kills somebody adjacent to you with the vilest of magick. It has a long cooldown.

Disable Technology:
This spell disables all weapons, cameras and most other technology in range.

Smoke:
This spell spawns a cloud of choking smoke at your location and does not require wizard garb.

Blind:
This spell temporarly blinds a single person and does not require wizard garb.

Forcewall:
This spell creates an unbreakable wall that lasts for 30 seconds and does not require wizard garb.

Blink:
This spell randomly teleports you a short distance. Useful for evasion or getting into areas if you have patience.

Teleport:
This spell teleports you to a type of area of your selection. Very useful if you are in danger, but has a decent cooldown, and is unpredictable.

Mutate:
This spell causes you to turn into a hulk, and gain telekinesis for a short while.

Ethereal Jaunt:
This spell creates your ethereal form, temporarily making you invisible and able to pass through walls.

Knock:
This spell opens nearby doors and does not require wizard garb.

"; name = "List of Available Spells (READ)"},/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) -"css" = (/obj/structure/bookcase{name = "bookcase (Adult)"},/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) +"css" = (/obj/structure/bookcase{category = "Adult"; name = "bookcase (Adult)"},/turf/unsimulated/floor{icon_state = "grimy"},/area/wizard_station) "cst" = (/turf/unsimulated/floor{dir = 9; icon_state = "carpetside"},/area/wizard_station) "csu" = (/obj/structure/stool,/turf/unsimulated/floor{dir = 1; icon_state = "carpetside"},/area/wizard_station) "csv" = (/turf/unsimulated/wall{icon = 'walls.dmi'; icon_state = "rock"; name = "grass"},/area/planet/clown)