diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 41410a62..b592fdfc 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -536,7 +536,6 @@ /obj/machinery/computer/security/telescreen/entertainment{ pixel_y = 32 }, -/obj/machinery/vr_sleeper, /turf/open/floor/plasteel, /area/security/prison) "abn" = ( @@ -3073,9 +3072,8 @@ /turf/open/floor/plasteel, /area/security/prison) "afQ" = ( -/obj/machinery/vr_sleeper, -/turf/open/floor/plasteel, -/area/security/prison) +/turf/closed/mineral/random/low_chance, +/area/space) "afR" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 1 @@ -4492,12 +4490,16 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) "aiE" = ( -/obj/machinery/vr_sleeper, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 +/obj/structure/closet/crate{ + name = "Asshole Containment" }, -/turf/open/floor/plasteel, -/area/crew_quarters/fitness/recreation) +/obj/item/banhammer, +/obj/item/toy/plush/mammal/winfre{ + item_state = "Winfre"; + name = "Winfre, Destroyer of Fun" + }, +/turf/open/floor/plating/asteroid/airless, +/area/space) "aiF" = ( /obj/machinery/atmospherics/pipe/manifold/supply/hidden{ dir = 8 @@ -5602,9 +5604,16 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) "akB" = ( -/obj/machinery/vr_sleeper, -/turf/open/floor/plasteel, -/area/crew_quarters/fitness/recreation) +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/door/poddoor/preopen{ + id = "engsm"; + name = "radiation shutters" + }, +/obj/item/tank/internals/plasma/full, +/turf/open/floor/plating, +/area/engine/supermatter) "akC" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/machinery/holopad, @@ -6384,7 +6393,6 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) "alS" = ( -/obj/machinery/vr_sleeper, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -11113,7 +11121,7 @@ /turf/open/floor/plating, /area/maintenance/port/fore) "auE" = ( -/obj/machinery/computer/gulag_teleporter_computer{ +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 1 }, /turf/open/floor/plasteel/dark, @@ -62923,6 +62931,7 @@ /obj/structure/cable/yellow{ icon_state = "1-8" }, +/obj/machinery/bloodbankgen, /turf/open/floor/plasteel/white, /area/medical/chemistry) "cqw" = ( @@ -68690,6 +68699,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/machinery/chem_dispenser, /turf/open/floor/plasteel/white, /area/medical/medbay/aft) "cAR" = ( @@ -77051,11 +77061,10 @@ /turf/open/floor/plasteel/white, /area/medical/virology) "cQM" = ( -/obj/effect/spawner/structure/window/reinforced, /obj/structure/cable/yellow{ icon_state = "0-8" }, -/turf/open/floor/plating, +/turf/closed/wall/r_wall, /area/medical/virology) "cQN" = ( /turf/closed/wall, @@ -77850,7 +77859,6 @@ /turf/open/floor/plating/airless, /area/medical/virology) "cSr" = ( -/obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 9 }, @@ -77860,7 +77868,7 @@ /obj/structure/cable/yellow{ icon_state = "0-4" }, -/turf/open/floor/plating, +/turf/closed/wall/r_wall, /area/medical/virology) "cSs" = ( /obj/machinery/atmospherics/components/unary/tank/air{ @@ -105073,7 +105081,7 @@ abl abE acd aax -afQ +aaR aaR aaR aaI @@ -119734,9 +119742,9 @@ acP afz agz ahQ -aiE -aiE -akB +ahP +ahP +alO alS alS aou @@ -125922,7 +125930,7 @@ aCF aDR aFi aGI -aIv +akB aJB aKP aLY @@ -138453,9 +138461,9 @@ aaa aaa aaa aaa -aaa -aaa -aaa +afQ +afQ +afQ aaa aaa aaa @@ -138709,11 +138717,11 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa +afQ +afQ +afQ +afQ +afQ aaa aaa aaa @@ -138965,13 +138973,13 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +afQ +afQ +afQ +aiE +afQ +afQ +afQ aaa aaa aaa @@ -139222,12 +139230,12 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa +afQ +afQ +afQ +afQ +afQ +afQ aaa aaa aaa @@ -139480,10 +139488,10 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa +afQ +afQ +afQ +afQ aaa aaa aaa diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 87c97cf8..e4ec5716 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -23,7 +23,7 @@ "aw" = (/obj/structure/flora/bush,/turf/open/floor/holofloor/snow,/area/holodeck/rec_center/winterwonderland) "ax" = (/obj/effect/holodeck_effect/mobspawner/penguin,/obj/effect/holodeck_effect/mobspawner/penguin,/obj/item/toy/snowball{pixel_y = 6},/obj/item/toy/snowball{pixel_x = 5},/obj/item/toy/snowball{pixel_x = -4},/turf/open/floor/holofloor/snow,/area/holodeck/rec_center/winterwonderland) "ay" = (/obj/structure/table,/obj/machinery/recharger,/turf/open/floor/holofloor/asteroid,/area/holodeck/rec_center/bunker) -"az" = (/obj/structure/table/wood,/obj/item/storage/box/matches{pixel_x = -4; pixel_y = 8},/turf/open/floor/holofloor{icon_state = "wood"; dir = 9},/area/holodeck/rec_center/lounge) +"az" = (/obj/structure/table/wood,/turf/open/floor/holofloor{icon_state = "wood"; dir = 9},/area/holodeck/rec_center/lounge) "aA" = (/obj/structure/chair/wood/normal,/turf/open/floor/holofloor{icon_state = "wood"; dir = 9},/area/holodeck/rec_center/lounge) "aB" = (/obj/effect/holodeck_effect/mobspawner,/turf/open/floor/holofloor/plating,/area/holodeck/rec_center/wildlife) "aC" = (/obj/effect/holodeck_effect/sparks,/turf/open/floor/holofloor/plating/burnmix,/area/holodeck/rec_center/burn) diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 7e546218..895d1e0d 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -70,6 +70,7 @@ #define ADMIN_PUNISHMENT_FIREBALL "Fireball" #define ADMIN_PUNISHMENT_CLUWNE "Floor Cluwne" #define ADMIN_PUNISHMENT_ROD "Immovable Rod" +#define ADMIN_PUNISHMENT_SUPPLYPOD_QUICK "Supply Pod (Quick)" #define ADMIN_PUNISHMENT_SUPPLYPOD "Supply Pod" #define ADMIN_PUNISHMENT_MAZING "Puzzle" #define ADMIN_PUNISHMENT_PIE "Cream Pie" @@ -93,3 +94,4 @@ #define MAX_KEYPRESS_AUTOKICK 100 ///Length of held key rolling buffer #define HELD_KEY_BUFFER_LENGTH 15 + diff --git a/code/__DEFINES/cargo.dm b/code/__DEFINES/cargo.dm index e87c4d3e..4317a922 100644 --- a/code/__DEFINES/cargo.dm +++ b/code/__DEFINES/cargo.dm @@ -11,6 +11,7 @@ #define STYLE_FRUIT 11 #define STYLE_INVISIBLE 12 #define STYLE_GONDOLA 13 +#define STYLE_SEETHROUGH 14 #define POD_ICON_STATE 1 #define POD_NAME 2 @@ -29,5 +30,6 @@ list("honkpod", "\improper HONK pod", "A brightly-colored supply pod. It likely originated from the Clown Federation."),\ list("fruitpod", "\improper Orange", "An angry orange."),\ list("", "\improper S.T.E.A.L.T.H. pod MKVII", "A supply pod that, under normal circumstances, is completely invisible to conventional methods of detection. How are you even seeing this?"),\ - list("gondolapod", "gondola", "The silent walker. This one seems to be part of a delivery agency.")\ + list("gondolapod", "gondola", "The silent walker. This one seems to be part of a delivery agency."),\ + list("", "", "")\ ) \ No newline at end of file diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 84a359f6..311f220f 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -32,6 +32,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define TESLA_IGNORE_1 (1<<13) // TESLA_IGNORE grants immunity from being targeted by tesla-style electricity #define INITIALIZED_1 (1<<14) //Whether /atom/Initialize() has already run for the object #define ADMIN_SPAWNED_1 (1<<15) //was this spawned by an admin? used for stat tracking stuff. +#define PREVENT_CONTENTS_EXPLOSION_1 (1<<16) /// should not get harmed if this gets caught by an explosion? //turf-only flags #define NOJAUNT_1 (1<<0) @@ -91,3 +92,4 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 // radiation #define RAD_PROTECT_CONTENTS (1<<0) #define RAD_NO_CONTAMINATE (1<<1) + diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 752e4e56..aaec389d 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -478,6 +478,11 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define AREASELECT_CORNERA "corner A" #define AREASELECT_CORNERB "corner B" +#define VARSET_FROM_LIST(L, V) if(L && L[#V]) V = L[#V] +#define VARSET_FROM_LIST_IF(L, V, C...) if(L && L[#V] && (C)) V = L[#V] +#define VARSET_TO_LIST(L, V) if(L) L[#V] = V +#define VARSET_TO_LIST_IF(L, V, C...) if(L && (C)) L[#V] = V + #define PREF_SAVELOAD_COOLDOWN 5 #define VOMIT_TOXIC 1 diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index ee15c0ab..813f0e09 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -573,3 +573,9 @@ L1[key] += other_value else L1[key] = other_value + +/proc/assoc_list_strip_value(list/input) + var/list/ret = list() + for(var/key in input) + ret += key + return ret \ No newline at end of file diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 541b3d56..87b68a92 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -510,7 +510,7 @@ var/jobtext = "" if(ply.assigned_role) jobtext = " the [ply.assigned_role]" - var/text = "[ply.key] was [ply.name][jobtext] and" + var/text = "[ply.hide_ckey ? "[ply.name][jobtext] " : "[ply.key] was [ply.name][jobtext] and "]" if(ply.current) if(ply.current.stat == DEAD) text += " died" diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index dabaa908..4bac0014 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -1,27 +1,34 @@ - SUBSYSTEM_DEF(research) name = "Research" priority = FIRE_PRIORITY_RESEARCH wait = 10 init_order = INIT_ORDER_RESEARCH + //TECHWEB STATIC + var/list/techweb_nodes = list() //associative id = node datum + var/list/techweb_designs = list() //associative id = node datum + var/list/datum/techweb/techwebs = list() + var/datum/techweb/science/science_tech + var/datum/techweb/admin/admin_tech + var/datum/techweb_node/error_node/error_node //These two are what you get if a node/design is deleted and somehow still stored in a console. + var/datum/design/error_design/error_design + + //ERROR LOGGING var/list/invalid_design_ids = list() //associative id = number of times var/list/invalid_node_ids = list() //associative id = number of times var/list/invalid_node_boost = list() //associative id = error message + var/list/obj/machinery/rnd/server/servers = list() - var/datum/techweb/science/science_tech - var/datum/techweb/admin/admin_tech - var/list/techweb_nodes = list() //associative id = node datum - var/list/techweb_categories = list() //category name = list(node.id = node) - var/list/techweb_designs = list() //associative id = node datum - var/list/techweb_nodes_starting = list() //associative id = node datum + + var/list/techweb_nodes_starting = list() //associative id = TRUE + var/list/techweb_categories = list() //category name = list(node.id = TRUE) var/list/techweb_boost_items = list() //associative double-layer path = list(id = list(point_type = point_discount)) - var/list/techweb_nodes_hidden = list() //Nodes that should be hidden by default. + var/list/techweb_nodes_hidden = list() //Node ids that should be hidden by default. var/list/techweb_point_items = list( //path = list(point type = value) /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 2500), /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 5000), // Cit three more anomalys anomalys /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 7500), /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 10000), - // - Slime Extracts! - + // - Slime Extracts! - /obj/item/slime_extract/grey = list(TECHWEB_POINT_TYPE_GENERIC = 500), // Adds in slime core deconing /obj/item/slime_extract/metal = list(TECHWEB_POINT_TYPE_GENERIC = 750), /obj/item/slime_extract/purple = list(TECHWEB_POINT_TYPE_GENERIC = 750), @@ -294,6 +301,8 @@ SUBSYSTEM_DEF(research) science_tech = new /datum/techweb/science admin_tech = new /datum/techweb/admin autosort_categories() + error_design = new + error_node = new return ..() /datum/controller/subsystem/research/fire() @@ -330,6 +339,191 @@ SUBSYSTEM_DEF(research) for(var/i in techweb_nodes) var/datum/techweb_node/I = techweb_nodes[i] if(techweb_categories[I.category]) - techweb_categories[I.category][I.id] = I + techweb_categories[I.category][I.id] = TRUE else - techweb_categories[I.category] = list(I.id = I) + techweb_categories[I.category] = list(I.id = TRUE) + +/datum/controller/subsystem/research/proc/techweb_node_by_id(id) + return techweb_nodes[id] || error_node + +/datum/controller/subsystem/research/proc/techweb_design_by_id(id) + return techweb_designs[id] || error_design + +/datum/controller/subsystem/research/proc/on_design_deletion(datum/design/D) + for(var/i in techweb_nodes) + var/datum/techweb_node/TN = techwebs[i] + TN.on_design_deletion(TN) + for(var/i in techwebs) + var/datum/techweb/T = i + T.recalculate_nodes(TRUE) + +/datum/controller/subsystem/research/proc/on_node_deletion(datum/techweb_node/TN) + for(var/i in techweb_nodes) + var/datum/techweb_node/TN2 = techwebs[i] + TN2.on_node_deletion(TN) + for(var/i in techwebs) + var/datum/techweb/T = i + T.recalculate_nodes(TRUE) + +/datum/controller/subsystem/research/proc/initialize_all_techweb_nodes(clearall = FALSE) + if(islist(techweb_nodes) && clearall) + QDEL_LIST(techweb_nodes) + if(islist(techweb_nodes_starting && clearall)) + techweb_nodes_starting.Cut() + var/list/returned = list() + for(var/path in subtypesof(/datum/techweb_node)) + var/datum/techweb_node/TN = path + if(isnull(initial(TN.id))) + continue + TN = new path + if(returned[initial(TN.id)]) + stack_trace("WARNING: Techweb node ID clash with ID [initial(TN.id)] detected! Path: [path]") + errored_datums[TN] = initial(TN.id) + continue + returned[initial(TN.id)] = TN + if(TN.starting_node) + techweb_nodes_starting[TN.id] = TRUE + for(var/id in techweb_nodes) + var/datum/techweb_node/TN = techweb_nodes[id] + TN.Initialize() + techweb_nodes = returned + if (!verify_techweb_nodes()) //Verify all nodes have ids and such. + stack_trace("Invalid techweb nodes detected") + calculate_techweb_nodes() + calculate_techweb_boost_list() + if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly. + CRASH("Invalid techweb nodes detected") + +/datum/controller/subsystem/research/proc/initialize_all_techweb_designs(clearall = FALSE) + if(islist(techweb_designs) && clearall) + QDEL_LIST(techweb_designs) + var/list/returned = list() + for(var/path in subtypesof(/datum/design)) + var/datum/design/DN = path + if(isnull(initial(DN.id))) + stack_trace("WARNING: Design with null ID detected. Build path: [initial(DN.build_path)]") + continue + else if(initial(DN.id) == DESIGN_ID_IGNORE) + continue + DN = new path + if(returned[initial(DN.id)]) + stack_trace("WARNING: Design ID clash with ID [initial(DN.id)] detected! Path: [path]") + errored_datums[DN] = initial(DN.id) + continue + returned[initial(DN.id)] = DN + techweb_designs = returned + verify_techweb_designs() + +/datum/controller/subsystem/research/proc/verify_techweb_nodes() + . = TRUE + for(var/n in techweb_nodes) + var/datum/techweb_node/N = techweb_nodes[n] + if(!istype(N)) + WARNING("Invalid research node with ID [n] detected and removed.") + techweb_nodes -= n + research_node_id_error(n) + . = FALSE + for(var/p in N.prereq_ids) + var/datum/techweb_node/P = techweb_nodes[p] + if(!istype(P)) + WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.") + N.prereq_ids -= p + research_node_id_error(p) + . = FALSE + for(var/d in N.design_ids) + var/datum/design/D = techweb_designs[d] + if(!istype(D)) + WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.") + N.design_ids -= d + design_id_error(d) + . = FALSE + for(var/u in N.unlock_ids) + var/datum/techweb_node/U = techweb_nodes[u] + if(!istype(U)) + WARNING("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.") + N.unlock_ids -= u + research_node_id_error(u) + . = FALSE + for(var/p in N.boost_item_paths) + if(!ispath(p)) + N.boost_item_paths -= p + WARNING("[p] is not a valid path.") + node_boost_error(N.id, "[p] is not a valid path.") + . = FALSE + var/list/points = N.boost_item_paths[p] + if(islist(points)) + for(var/i in points) + if(!isnum(points[i])) + WARNING("[points[i]] is not a valid number.") + node_boost_error(N.id, "[points[i]] is not a valid number.") + . = FALSE + else if(!point_types[i]) + WARNING("[i] is not a valid point type.") + node_boost_error(N.id, "[i] is not a valid point type.") + . = FALSE + else if(!isnull(points)) + N.boost_item_paths -= p + node_boost_error(N.id, "No valid list.") + WARNING("No valid list.") + . = FALSE + CHECK_TICK + +/datum/controller/subsystem/research/proc/verify_techweb_designs() + for(var/d in techweb_designs) + var/datum/design/D = techweb_designs[d] + if(!istype(D)) + stack_trace("WARNING: Invalid research design with ID [d] detected and removed.") + techweb_designs -= d + CHECK_TICK + +/datum/controller/subsystem/research/proc/research_node_id_error(id) + if(invalid_node_ids[id]) + invalid_node_ids[id]++ + else + invalid_node_ids[id] = 1 + +/datum/controller/subsystem/research/proc/design_id_error(id) + if(invalid_design_ids[id]) + invalid_design_ids[id]++ + else + invalid_design_ids[id] = 1 + +/datum/controller/subsystem/research/proc/calculate_techweb_nodes() + for(var/design_id in techweb_designs) + var/datum/design/D = techweb_designs[design_id] + D.unlocked_by.Cut() + for(var/node_id in techweb_nodes) + var/datum/techweb_node/node = techweb_nodes[node_id] + node.unlock_ids = list() + for(var/i in node.design_ids) + var/datum/design/D = techweb_designs[i] + node.design_ids[i] = TRUE + D.unlocked_by += node.id + if(node.hidden) + techweb_nodes_hidden[node.id] = TRUE + CHECK_TICK + generate_techweb_unlock_linking() + +/datum/controller/subsystem/research/proc/generate_techweb_unlock_linking() + for(var/node_id in techweb_nodes) //Clear all unlock links to avoid duplication. + var/datum/techweb_node/node = techweb_nodes[node_id] + node.unlock_ids = list() + for(var/node_id in techweb_nodes) + var/datum/techweb_node/node = techweb_nodes[node_id] + for(var/prereq_id in node.prereq_ids) + var/datum/techweb_node/prereq_node = techweb_node_by_id(prereq_id) + prereq_node.unlock_ids[node.id] = node + +/datum/controller/subsystem/research/proc/calculate_techweb_boost_list(clearall = FALSE) + if(clearall) + techweb_boost_items = list() + for(var/node_id in techweb_nodes) + var/datum/techweb_node/node = techweb_nodes[node_id] + for(var/path in node.boost_item_paths) + if(!ispath(path)) + continue + if(length(techweb_boost_items[path])) + techweb_boost_items[path][node.id] = node.boost_item_paths[path] + else + techweb_boost_items[path] = list(node.id = node.boost_item_paths[path]) + CHECK_TICK \ No newline at end of file diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index b3c57e48..7be4489e 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -198,7 +198,7 @@ GLOBAL_LIST_EMPTY(explosions) var/list/items = list() for(var/I in T) var/atom/A = I - if (!A.prevent_content_explosion()) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't. + if (!(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't. items += A.GetAllContents() for(var/O in items) var/atom/A = O diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 3b549958..0e4e9319 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -41,6 +41,8 @@ var/special_role var/list/restricted_roles = list() + var/hide_ckey = FALSE + var/list/datum/objective/objectives = list() var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. @@ -134,6 +136,8 @@ L.canbearoused = L.client.prefs.arousable //Technically this should make taking over a character mean the body gain the new minds setting... L.update_arousal_hud() //Removes the old icon + hide_ckey = current.client?.prefs?.hide_ckey + /datum/mind/proc/store_memory(new_text) if((length_char(memory) + length_char(new_text)) <= MAX_MESSAGE_LEN) memory += "[new_text]
" @@ -853,6 +857,7 @@ if(!mind.name) mind.name = real_name mind.current = src + mind.hide_ckey = client?.prefs?.hide_ckey /mob/living/carbon/mind_initialize() ..() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index c63c5b3e..d3c530cb 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -345,9 +345,6 @@ to_chat(user, "You can't move while buckled to [src]!") return -/atom/proc/prevent_content_explosion() - return FALSE - /atom/proc/contents_explosion(severity, target) return //For handling the effects of explosions on contents that would not normally be effected @@ -875,3 +872,4 @@ Proc for attack log creation, because really why not /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) + diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm index e784fd83..68be5715 100644 --- a/code/game/gamemodes/bloodsucker/bloodsucker.dm +++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm @@ -17,7 +17,7 @@ antag_flag = ROLE_BLOODSUCKER false_report_weight = 1 restricted_jobs = list("AI","Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_jobs = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_players = 20 required_enemies = 2 recommended_enemies = 4 diff --git a/code/game/gamemodes/bloodsucker/hunter.dm b/code/game/gamemodes/bloodsucker/hunter.dm index 6784247b..bb87b0af 100644 --- a/code/game/gamemodes/bloodsucker/hunter.dm +++ b/code/game/gamemodes/bloodsucker/hunter.dm @@ -11,7 +11,7 @@ var/list/no_hunter_jobs = list("AI","Cyborg") // Set Restricted Jobs if(CONFIG_GET(flag/protect_roles_from_antagonist)) - no_hunter_jobs += list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + no_hunter_jobs += list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") if(CONFIG_GET(flag/protect_assistant_from_antagonist)) no_hunter_jobs += "Assistant" // Find Valid Hunters diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm index b0d46100..c0db6008 100644 --- a/code/game/gamemodes/brother/traitor_bro.dm +++ b/code/game/gamemodes/brother/traitor_bro.dm @@ -6,7 +6,7 @@ name = "traitor+brothers" config_tag = "traitorbro" restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Research Director") + protected_jobs = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Research Director") announce_span = "danger" announce_text = "There are Syndicate agents and Blood Brothers on the station!\n\ diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index b974c18a..5a3d6268 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -11,7 +11,7 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th antag_flag = ROLE_CHANGELING false_report_weight = 10 restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //citadel change - adds HoP, CE, CMO, and RD to ling role blacklist + protected_jobs = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //citadel change - adds HoP, CE, CMO, and RD to ling role blacklist required_players = 15 required_enemies = 1 recommended_enemies = 4 diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 8ba449c9..f59f2d87 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -54,7 +54,7 @@ antag_datum = /datum/antagonist/traitor antag_flag = ROLE_TRAITOR restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 weight = 7 cost = 5 @@ -108,4 +108,4 @@ new_head.remove_clumsy = TRUE new_head = M.mind.add_antag_datum(new_head) new_head.rev_team.max_headrevs = 1 // Only one revhead if it is latejoin. - return TRUE \ No newline at end of file + return TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index ceb9eb22..cc5c5889 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -208,7 +208,7 @@ antag_datum = /datum/antagonist/traitor antag_flag = ROLE_TRAITOR restricted_roles = list("AI", "Cyborg", "Positronic Brain") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 weight = 4 cost = 10 @@ -224,7 +224,7 @@ antag_datum = /datum/antagonist/traitor/thief antag_flag = ROLE_TRAITOR restricted_roles = list("AI", "Cyborg", "Positronic Brain") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 weight = 4 cost = 5 @@ -545,7 +545,7 @@ antag_datum = /datum/antagonist/cult minimum_required_age = 14 restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 weight = 5 cost = 20 @@ -823,7 +823,7 @@ datum/dynamic_ruleset/midround/bloodcult/trim_candidates() antag_datum = ANTAG_DATUM_BLOODSUCKER antag_flag = ROLE_TRAITOR restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 enemy_roles = list("Security Officer","Head of Security","Captain","AI","Cyborg","Chaplain","Curator") required_enemies = list(3,2,2,2,2,2,2,2,2,2) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index bafccca2..8d32c20d 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -11,7 +11,7 @@ antag_flag = ROLE_TRAITOR antag_datum = /datum/antagonist/traitor/ minimum_required_age = 0 - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Cyborg", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Cyborg", "Quartermaster") restricted_roles = list("Cyborg", "AI") //Malf is it's own ruleset required_candidates = 1 weight = 5 @@ -28,7 +28,7 @@ antag_flag = ROLE_TRAITOR antag_datum = /datum/antagonist/traitor/thief minimum_required_age = 0 - //protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Cyborg") + //protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Cyborg") restricted_roles = list("Cyborg", "AI") //Malf is it's own ruleset //required_candidates = 1 weight = 5 @@ -127,7 +127,7 @@ antag_flag = ROLE_BROTHER antag_datum = /datum/antagonist/brother/ restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 2 weight = 4 cost = 10 @@ -181,7 +181,7 @@ antag_flag = ROLE_CHANGELING antag_datum = /datum/antagonist/changeling restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 weight = 3 cost = 10 @@ -279,7 +279,7 @@ antag_datum = /datum/antagonist/cult minimum_required_age = 14 restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 2 weight = 1 cost = 30 @@ -839,7 +839,7 @@ antag_flag = ROLE_BLOODSUCKER antag_datum = ANTAG_DATUM_BLOODSUCKER minimum_required_age = 0 - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + protected_roles = list("Rookie", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") restricted_roles = list("Cyborg", "AI") required_candidates = 1 weight = 3 diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 29faa1f7..3def90d3 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -176,7 +176,7 @@ matching_designs.Cut() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(findtext(D.name,href_list["to_search"])) matching_designs.Add(D) updateUsrDialog() @@ -252,7 +252,7 @@ dat += materials_printout() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category)) continue @@ -366,7 +366,7 @@ /obj/machinery/autolathe/proc/adjust_hacked(state) hacked = state for(var/id in SSresearch.techweb_designs) - var/datum/design/D = SSresearch.techweb_designs[id] + var/datum/design/D = SSresearch.techweb_design_by_id(id) if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) if(hacked) stored_research.add_design(D) diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 5d04e042..7439fd1b 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -31,7 +31,7 @@ /obj/machinery/computer/operating/proc/sync_surgeries() for(var/i in linked_techweb.researched_designs) - var/datum/design/surgery/D = linked_techweb.researched_designs[i] + var/datum/design/surgery/D = SSresearch.techweb_design_by_id(i) if(!istype(D)) continue advanced_surgeries |= D.surgery diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm index a5b4b36e..7c51070d 100644 --- a/code/game/machinery/limbgrower.dm +++ b/code/game/machinery/limbgrower.dm @@ -177,7 +177,7 @@ dat += materials_printout() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category)) continue if(disabled || !can_build(D)) @@ -219,7 +219,7 @@ if(obj_flags & EMAGGED) return for(var/id in SSresearch.techweb_designs) - var/datum/design/D = SSresearch.techweb_designs[id] + var/datum/design/D = SSresearch.techweb_design_by_id(id) if((D.build_type & LIMBGROWER) && ("emagged" in D.category)) stored_research.add_design(D) to_chat(user, "A warning flashes onto the screen, stating that safety overrides have been deactivated!") diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index f28bd629..611f3d36 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -85,7 +85,7 @@ /obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name) var/output = "" for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(D.build_type & MECHFAB) if(!(set_name in D.category)) continue @@ -183,7 +183,7 @@ /obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name) if(set_name in part_sets) for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(D.build_type & MECHFAB) if(set_name in D.category) add_to_queue(D) @@ -338,7 +338,7 @@ if(href_list["part"]) var/T = afilter.getStr("part") for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(D.build_type & MECHFAB) if(D.id == T) if(!processing_queue) @@ -349,7 +349,7 @@ if(href_list["add_to_queue"]) var/T = afilter.getStr("add_to_queue") for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(D.build_type & MECHFAB) if(D.id == T) add_to_queue(D) @@ -387,7 +387,7 @@ if(href_list["part_desc"]) var/T = afilter.getStr("part_desc") for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(D.build_type & MECHFAB) if(D.id == T) var/obj/part = D.build_path @@ -404,11 +404,11 @@ updateUsrDialog() return -/obj/machinery/mecha_part_fabricator/proc/do_process_queue() - if(processing_queue || being_built) - return FALSE +/obj/machinery/mecha_part_fabricator/proc/do_process_queue() + if(processing_queue || being_built) + return FALSE processing_queue = 1 - process_queue() + process_queue() processing_queue = 0 /obj/machinery/mecha_part_fabricator/proc/eject_sheets(eject_sheet, eject_amt) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 30fec05d..762dd7e7 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -213,7 +213,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/list/boostable_nodes = techweb_item_boost_check(src) if (boostable_nodes) for(var/id in boostable_nodes) - var/datum/techweb_node/node = SSresearch.techweb_nodes[id] + var/datum/techweb_node/node = SSresearch.techweb_node_by_id(id) + if(!node) + continue research_msg += sep research_msg += node.display_name sep = ", " diff --git a/code/game/objects/items/implants/implant_CQC.dm b/code/game/objects/items/implants/implant_CQC.dm new file mode 100644 index 00000000..563a1600 --- /dev/null +++ b/code/game/objects/items/implants/implant_CQC.dm @@ -0,0 +1,38 @@ +/obj/item/implant/cqc + name = "CQC implant" + desc = "Teaches you the arts of CQC in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/library.dmi' + icon_state ="cqcmanual" + activated = 1 + var/datum/martial_art/cqc/style = new + +/obj/item/implant/cqc/get_data() + var/dat = {"Implant Specifications:
+ Name: CQC Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of CQC."} + return dat + +/obj/item/implant/cqc/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_CQC)) + style.remove(H) + to_chat(H, "You forget the basics of CQC.") + else + style.teach(H,1) + to_chat(H, "You remember the basics of CQC!") + +/obj/item/implanter/cqc + name = "implanter (CQC)" + imp_type = /obj/item/implant/cqc + +/obj/item/implantcase/cqc + name = "implant case - 'CQC'" + desc = "A glass case containing an implant that can teach the user the arts of CQC." + imp_type = /obj/item/implant/cqc diff --git a/code/game/objects/items/implants/implant_krav_maga.dm b/code/game/objects/items/implants/implant_krav_maga.dm index 373658b3..13bd4afa 100644 --- a/code/game/objects/items/implants/implant_krav_maga.dm +++ b/code/game/objects/items/implants/implant_krav_maga.dm @@ -1,8 +1,8 @@ /obj/item/implant/krav_maga name = "krav maga implant" desc = "Teaches you the arts of Krav Maga in 5 short instructional videos beamed directly into your eyeballs." - icon = 'icons/obj/wizard.dmi' - icon_state ="scroll2" + icon = 'icons/obj/clothing/gloves.dmi' + icon_state ="fightgloves" activated = 1 var/datum/martial_art/krav_maga/style = new diff --git a/code/game/objects/items/implants/implant_mushpunch.dm b/code/game/objects/items/implants/implant_mushpunch.dm new file mode 100644 index 00000000..b7f0a689 --- /dev/null +++ b/code/game/objects/items/implants/implant_mushpunch.dm @@ -0,0 +1,38 @@ +/obj/item/implant/mushpunch + name = "Mushroom Punch implant" + desc = "Teaches you the arts of Mushroom Punch in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state ="angel" + activated = 1 + var/datum/martial_art/mushpunch/style = new + +/obj/item/implant/mushpunch/get_data() + var/dat = {"Implant Specifications:
+ Name: Mushroom Punch Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of Mushroom Punch."} + return dat + +/obj/item/implant/mushpunch/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_MUSHPUNCH)) + style.remove(H) + to_chat(H, "You forget the teachings of Mushroom Punch.") + else + style.teach(H,1) + to_chat(H, "You learn the arts of Mushroom Punch!") + +/obj/item/implanter/mushpunch + name = "implanter (Mushroom Punch)" + imp_type = /obj/item/implant/mushpunch + +/obj/item/implantcase/mushpunch + name = "implant case - 'Mushroom Punch'" + desc = "A glass case containing an implant that can teach the user the arts of Mushroom Punch." + imp_type = /obj/item/implant/mushpunch diff --git a/code/game/objects/items/implants/implant_plasmafist.dm b/code/game/objects/items/implants/implant_plasmafist.dm new file mode 100644 index 00000000..7d6bccaa --- /dev/null +++ b/code/game/objects/items/implants/implant_plasmafist.dm @@ -0,0 +1,38 @@ +/obj/item/implant/plasmafist + name = "Plasma Fist implant" + desc = "Teaches you the arts of Plasma Fist in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state ="disintegrate" + activated = 1 + var/datum/martial_art/plasma_fist/style = new + +/obj/item/implant/plasmafist/get_data() + var/dat = {"Implant Specifications:
+ Name: Plasma Fist Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of Plasma Fist."} + return dat + +/obj/item/implant/plasmafist/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_PLASMAFIST)) + style.remove(H) + to_chat(H, "You forget the teachings of the Plasma Fist.") + else + style.teach(H,1) + to_chat(H, "You learn the arts of the Plasma Fist!") + +/obj/item/implanter/plasmafist + name = "implanter (Plasma Fist)" + imp_type = /obj/item/implant/plasmafist + +/obj/item/implantcase/plasmafist + name = "implant case - 'Plasma Fist'" + desc = "A glass case containing an implant that can teach the user the arts of Plasma Fist." + imp_type = /obj/item/implant/plasmafist diff --git a/code/game/objects/items/implants/implant_psychobrawl.dm b/code/game/objects/items/implants/implant_psychobrawl.dm new file mode 100644 index 00000000..4091879d --- /dev/null +++ b/code/game/objects/items/implants/implant_psychobrawl.dm @@ -0,0 +1,38 @@ +/obj/item/implant/psychobrawl + name = "Psychotic Brawling implant" + desc = "Teaches you the arts of Psychotic Brawling in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/surgery.dmi' + icon_state ="brain" + activated = 1 + var/datum/martial_art/psychotic_brawling/style = new + +/obj/item/implant/psychobrawl/get_data() + var/dat = {"Implant Specifications:
+ Name: Psychotic Brawling Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of Psychotic Brawling."} + return dat + +/obj/item/implant/psychobrawl/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_PSYCHOBRAWL)) + style.remove(H) + to_chat(H, "You forget the teachings of Psychotic Brawling.") + else + style.teach(H,1) + to_chat(H, "You learn the arts of Psychotic Brawling!") + +/obj/item/implanter/psychobrawl + name = "implanter (Psychotic Brawling)" + imp_type = /obj/item/implant/psychobrawl + +/obj/item/implantcase/psychobrawl + name = "implant case - 'Psychotic Brawling'" + desc = "A glass case containing an implant that can teach the user the arts of Psychotic Brawling." + imp_type = /obj/item/implant/psychobrawl diff --git a/code/game/objects/items/implants/implant_sleepingcarp.dm b/code/game/objects/items/implants/implant_sleepingcarp.dm new file mode 100644 index 00000000..e1ef60e1 --- /dev/null +++ b/code/game/objects/items/implants/implant_sleepingcarp.dm @@ -0,0 +1,38 @@ +/obj/item/implant/sleepingcarp + name = "Sleeping Carp implant" + desc = "Teaches you the arts of Sleeping Carp in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/wizard.dmi' + icon_state ="scroll2" + activated = 1 + var/datum/martial_art/the_sleeping_carp/style = new + +/obj/item/implant/sleepingcarp/get_data() + var/dat = {"Implant Specifications:
+ Name: Sleeping Carp Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of Sleeping Carp."} + return dat + +/obj/item/implant/sleepingcarp/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_SLEEPINGCARP)) + style.remove(H) + to_chat(H, "You forget the teachings of the Sleeping Carp.") + else + style.teach(H,1) + to_chat(H, "You learn the arts of the Sleeping Carp!") + +/obj/item/implanter/sleepingcarp + name = "implanter (Sleeping Carp)" + imp_type = /obj/item/implant/sleepingcarp + +/obj/item/implantcase/sleepingcarp + name = "implant case - 'Sleeping Carp'" + desc = "A glass case containing an implant that can teach the user the arts of Sleeping Carp." + imp_type = /obj/item/implant/sleepingcarp diff --git a/code/game/objects/items/implants/implant_wrestling.dm b/code/game/objects/items/implants/implant_wrestling.dm new file mode 100644 index 00000000..58cfcbfe --- /dev/null +++ b/code/game/objects/items/implants/implant_wrestling.dm @@ -0,0 +1,38 @@ +/obj/item/implant/wrestling + name = "Wrestling implant" + desc = "Teaches you the arts of Wrestling in 5 short instructional videos beamed directly into your eyeballs." + icon = 'icons/obj/clothing/masks.dmi' + icon_state ="luchador" + activated = 1 + var/datum/martial_art/wrestling/style = new + +/obj/item/implant/wrestling/get_data() + var/dat = {"Implant Specifications:
+ Name: Wrestling Implant
+ Life: 4 hours after death of host
+ Implant Details:
+ Function: Teaches even the clumsiest host the arts of Wrestling."} + return dat + +/obj/item/implant/wrestling/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_WRESTLING)) + style.remove(H) + to_chat(H, "You forget the teachings of Wrestling.") + else + style.teach(H,1) + to_chat(H, "You learn the arts of Wrestling!") + +/obj/item/implanter/wrestling + name = "implanter (Wrestling)" + imp_type = /obj/item/implant/wrestling + +/obj/item/implantcase/wrestling + name = "implant case - 'Wrestling'" + desc = "A glass case containing an implant that can teach the user the arts of Wrestling." + imp_type = /obj/item/implant/wrestling diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index ad244ec6..013883fe 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -710,20 +710,44 @@ if(!check_rights(R_SPAWN)) return + var/turf/T = get_turf(usr) var/chosen = pick_closest_path(object) if(!chosen) return if(ispath(chosen, /turf)) - var/turf/T = get_turf(usr.loc) T.ChangeTurf(chosen) else - var/atom/A = new chosen(usr.loc) + var/atom/A = new chosen(T) A.flags_1 |= ADMIN_SPAWNED_1 log_admin("[key_name(usr)] spawned [chosen] at [AREACOORD(usr)]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/admins/proc/podspawn_atom(object as text) + set category = "Debug" + set desc = "(atom path) Spawn an atom via supply drop" + set name = "Podspawn" + + if(!check_rights(R_SPAWN)) + return + + var/chosen = pick_closest_path(object) + if(!chosen) + return + var/turf/T = get_turf(usr) + + if(ispath(chosen, /turf)) + T.ChangeTurf(chosen) + else + var/obj/structure/closet/supplypod/centcompod/pod = new() + var/atom/A = new chosen(pod) + A.flags_1 |= ADMIN_SPAWNED_1 + new /obj/effect/abstract/DPtarget(T, pod) + + log_admin("[key_name(usr)] pod-spawned [chosen] at [AREACOORD(usr)]") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Podspawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /datum/admins/proc/spawn_cargo(object as text) set category = "Debug" set desc = "(atom path) Spawn a cargo crate" @@ -1011,3 +1035,4 @@ log_admin("Man up global: [key_name(usr)] told everybody to man up") message_admins("[key_name_admin(usr)] told everybody to man up.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Man Up Global") + diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 5116e9c6..0ab823e2 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -112,7 +112,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/roll_dices //CIT CHANGE - Adds dice verb )) GLOBAL_PROTECT(admin_verbs_spawn) -GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character)) +GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character)) GLOBAL_PROTECT(admin_verbs_server) GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer()) /world/proc/AVerbsServer() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 101d659c..c781cf31 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2399,7 +2399,7 @@ var/atom/target //Where the object will be spawned var/where = href_list["object_where"] - if (!( where in list("onfloor","inhand","inmarked") )) + if (!( where in list("onfloor","frompod","inhand","inmarked") )) where = "onfloor" @@ -2410,7 +2410,7 @@ where = "onfloor" target = usr - if("onfloor") + if("onfloor", "frompod") switch(href_list["offset_type"]) if ("absolute") target = locate(0 + X,0 + Y,0 + Z) @@ -2426,7 +2426,10 @@ else target = marked_datum + var/obj/structure/closet/supplypod/centcompod/pod if(target) + if(where == "frompod") + pod = new() for (var/path in paths) for (var/i = 0; i < number; i++) if(path in typesof(/turf)) @@ -2435,7 +2438,11 @@ if(N && obj_name) N.name = obj_name else - var/atom/O = new path(target) + var/atom/O + if(where == "frompod") + O = new path(pod) + else + O = new path(target) if(!QDELETED(O)) O.flags_1 |= ADMIN_SPAWNED_1 if(obj_dir) @@ -2455,6 +2462,8 @@ R.module.add_module(I, TRUE, TRUE) R.activate_module(I) + if(pod) + new /obj/effect/abstract/DPtarget(target, pod) if (number == 1) log_admin("[key_name(usr)] created a [english_list(paths)]") @@ -2981,3 +2990,4 @@ else to_chat(usr, "Failed to establish database connection. The changes will last only for the current round.") to_chat(usr, "Mentor removed.") + diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 7b736590..f03baa36 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1260,12 +1260,13 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, + ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD, - ADMIN_PUNISHMENT_TABLETIDESTATIONWIDE, - ADMIN_PUNISHMENT_FAKEBWOINK, - ADMIN_PUNISHMENT_NUGGET, + ADMIN_PUNISHMENT_TABLETIDESTATIONWIDE, + ADMIN_PUNISHMENT_FAKEBWOINK, + ADMIN_PUNISHMENT_NUGGET, ADMIN_PUNISHMENT_BREADIFY) var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list @@ -1304,6 +1305,22 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits var/turf/startT = spaceDebrisStartLoc(startside, T.z) var/turf/endT = spaceDebrisFinishLoc(startside, T.z) new /obj/effect/immovablerod(startT, endT,target) + if(ADMIN_PUNISHMENT_SUPPLYPOD_QUICK) + var/target_path = input(usr,"Enter typepath of an atom you'd like to send with the pod (type \"empty\" to send an empty pod):" ,"Typepath","/obj/item/reagent_containers/food/snacks/grown/harebell") as null|text + var/obj/structure/closet/supplypod/centcompod/pod = new() + pod.damage = 40 + pod.explosionSize = list(0,0,0,2) + pod.effectStun = TRUE + if (isnull(target_path)) //The user pressed "Cancel" + return + if (target_path != "empty")//if you didn't type empty, we want to load the pod with a delivery + var/delivery = text2path(target_path) + if(!ispath(delivery)) + delivery = pick_closest_path(target_path) + if(!delivery) + alert("ERROR: Incorrect / improper path given.") + new delivery(pod) + new /obj/effect/abstract/DPtarget(get_turf(target), pod) if(ADMIN_PUNISHMENT_SUPPLYPOD) var/datum/centcom_podlauncher/plaunch = new(usr) if(!holder) @@ -1316,6 +1333,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits plaunch.temp_pod.explosionSize = list(0,0,0,2) plaunch.temp_pod.effectStun = TRUE plaunch.ui_interact(usr) + return //We return here because punish_log() is handled by the centcom_podlauncher datum + if(ADMIN_PUNISHMENT_MAZING) if(!puzzle_imprison(target)) to_chat(usr,"Imprisonment failed!") @@ -1357,12 +1376,13 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits addtimer(CALLBACK(GLOBAL_PROC, .proc/breadify, target), BREADIFY_TIME) #undef BREADIFY_TIME + punish_log(target, punishment) - var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]." +/client/proc/punish_log(var/whom, var/punishment) + var/msg = "[key_name_admin(usr)] punished [key_name_admin(whom)] with [punishment]." message_admins(msg) - admin_ticket_log(target, msg) - log_admin("[key_name(usr)] punished [key_name(target)] with [punishment].") - + admin_ticket_log(whom, msg) + log_admin("[key_name(usr)] punished [key_name(whom)] with [punishment].") /client/proc/trigger_centcom_recall() if(!check_rights(R_ADMIN)) @@ -1432,3 +1452,4 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits else message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") + diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index e482aa00..92cc1cd1 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -19,7 +19,7 @@ //Variables declared to change how items in the launch bay are picked and launched. (Almost) all of these are changed in the ui_act proc //Some effect groups are choices, while other are booleans. This is because some effects can stack, while others dont (ex: you can stack explosion and quiet, but you cant stack ordered launch and random launch) /datum/centcom_podlauncher - var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object, /obj/effect/particle_effect/sparks, /obj/effect/DPtarget, /obj/effect/supplypod_selector )) + var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object, /obj/effect/particle_effect/sparks, /obj/effect/abstract/DPtarget, /obj/effect/supplypod_selector )) var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back var/client/holder //client of whoever is using this datum var/area/bay //What bay we're using to launch shit from. @@ -29,9 +29,10 @@ var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2) var/launcherActivated = FALSE //check if we've entered "launch mode" (when we click a pod is launched). Used for updating mouse cursor var/effectBurst = FALSE //Effect that launches 5 at once in a 3x3 area centered on the target + var/effectAnnounce = TRUE var/numTurfs = 0 //Counts the number of turfs with things we can launch in the chosen bay (in the centcom map) var/launchCounter = 1 //Used with the "Ordered" launch mode (launchChoice = 1) to see what item is launched - var/specificTarget //Do we want to target a specific mob instead of where we click? Also used for smiting + var/atom/specificTarget //Do we want to target a specific mob instead of where we click? Also used for smiting var/list/orderedArea = list() //Contains an ordered list of turfs in an area (filled in the createOrderedArea() proc), read top-left to bottom-right. Used for the "ordered" launch mode (launchChoice = 1) var/list/acceptableTurfs = list() //Contians a list of turfs (in the "bay" area on centcom) that have items that can be launched. Taken from orderedArea var/list/launchList = list() //Contains whatever is going to be put in the supplypod and fired. Taken from acceptableTurfs @@ -66,12 +67,14 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1) data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2) data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2) + data["fallDuration"] = temp_pod.fallDuration //How long the pod's falling animation lasts data["landingDelay"] = temp_pod.landingDelay //How long the pod takes to land after launching data["openingDelay"] = temp_pod.openingDelay //How long the pod takes to open after landing data["departureDelay"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom) data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod. data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands + data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands data["effectBluespace"] = temp_pod.bluespace //If true, the pod deletes (in a shower of sparks) after landing data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isnt displayed on the turf where the pod will land data["effectQuiet"] = temp_pod.effectQuiet //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) @@ -81,12 +84,14 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm data["effectReverse"] = temp_pod.reversing //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom data["effectTarget"] = specificTarget //Launches the pod at the turf of a specific mob target, rather than wherever the user clicked. Useful for smites data["effectName"] = temp_pod.adminNamed //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc) + data["effectAnnounce"] = effectAnnounce data["giveLauncher"] = launcherActivated //If true, the user is in launch mode, and whenever they click a pod will be launched (either at their mouse position or at a specific target) data["numObjects"] = numTurfs //Counts the number of turfs that contain a launchable object in the centcom supplypod bay + data["fallingSound"] = temp_pod.fallingSound != initial(temp_pod.fallingSound)//Admin sound to play as the pod falls data["landingSound"] = temp_pod.landingSound //Admin sound to play when the pod lands data["openingSound"] = temp_pod.openingSound //Admin sound to play when the pod opens data["leavingSound"] = temp_pod.leavingSound //Admin sound to play when the pod leaves - data["soundVolume"] = temp_pod.soundVolume != 50 //Admin sound to play when the pod leaves + data["soundVolume"] = temp_pod.soundVolume != initial(temp_pod.soundVolume) //Admin sound to play when the pod leaves return data /datum/centcom_podlauncher/ui_act(action, params) @@ -227,6 +232,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if("effectLimb") //Toggle: Anyone carbon mob under the pod loses a limb when it lands temp_pod.effectLimb = !temp_pod.effectLimb . = TRUE + if("effectOrgans") //Toggle: Any carbon mob under the pod loses every limb and organ + temp_pod.effectOrgans = !temp_pod.effectOrgans + . = TRUE if("effectBluespace") //Toggle: Deletes the pod after landing temp_pod.bluespace = !temp_pod.bluespace . = TRUE @@ -245,6 +253,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if("effectBurst") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target effectBurst = !effectBurst . = TRUE + if("effectAnnounce") //Toggle: Sends a ghost announcement. + effectAnnounce = !effectAnnounce + . = TRUE if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from temp_pod.reversing = !temp_pod.reversing . = TRUE @@ -261,11 +272,23 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm . = TRUE ////////////////////////////TIMER DELAYS////////////////// + if("fallDuration") //Change the falling animation duration + if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the fall duration has already been changed when we push the "change value" button, then set it to default + temp_pod.fallDuration = initial(temp_pod.fallDuration) + return + var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num + if (isnull(timeInput)) + return + if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default + alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.") + timeInput = initial(temp_pod.fallDuration) + temp_pod.fallDuration = 10 * timeInput + . = TRUE if("landingDelay") //Change the time it takes the pod to land, after firing if (temp_pod.landingDelay != initial(temp_pod.landingDelay)) //If the landing delay has already been changed when we push the "change value" button, then set it to default temp_pod.landingDelay = initial(temp_pod.landingDelay) return - var/timeInput = input("Delay Time", "Enter the time it takes for the pod to land, in seconds", 0.5) as null|num + var/timeInput = input("Enter the time it takes for the pod to land, in seconds", "Delay Time", initial(temp_pod.landingDelay) * 0.1) as null|num if (isnull(timeInput)) return if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default @@ -277,7 +300,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (temp_pod.openingDelay != initial(temp_pod.openingDelay)) //If the opening delay has already been changed when we push the "change value" button, then set it to default temp_pod.openingDelay = initial(temp_pod.openingDelay) return - var/timeInput = input("Delay Time", "Enter the time it takes for the pod to open after landing, in seconds", 3) as null|num + var/timeInput = input("Enter the time it takes for the pod to open after landing, in seconds", "Delay Time", initial(temp_pod.openingDelay) * 0.1) as null|num if (isnull(timeInput)) return if (!isnum(timeInput)) //Sanitize input @@ -289,7 +312,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (temp_pod.departureDelay != initial(temp_pod.departureDelay)) //If the departure delay has already been changed when we push the "change value" button, then set it to default temp_pod.departureDelay = initial(temp_pod.departureDelay) return - var/timeInput = input("Delay Time", "Enter the time it takes for the pod to leave after opening, in seconds", 3) as null|num + var/timeInput = input("Enter the time it takes for the pod to leave after opening, in seconds", "Delay Time", initial(temp_pod.departureDelay) * 0.1) as null|num if (isnull(timeInput)) return if (!isnum(timeInput)) @@ -299,31 +322,57 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm . = TRUE ////////////////////////////ADMIN SOUNDS////////////////// + if("fallingSound") //Admin sound from a local file that plays when the pod falls + if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound)) + temp_pod.fallingSound = initial(temp_pod.fallingSound) + temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength) + return + var/soundInput = input(holder, "Please pick a sound file to play when the pod lands! NOTICE: Take a note of exactly how long the sound is.", "Pick a Sound File") as null|sound + if (isnull(soundInput)) + return + var/timeInput = input(holder, "What is the exact length of the sound file, in seconds. This number will be used to line the sound up so that it finishes right as the pod lands!", "Pick a Sound File", 0.3) as null|num + if (isnull(timeInput)) + return + if (!isnum(timeInput)) + alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.") + temp_pod.fallingSound = soundInput + temp_pod.fallingSoundLength = 10 * timeInput + . = TRUE if("landingSound") //Admin sound from a local file that plays when the pod lands if (!isnull(temp_pod.landingSound)) temp_pod.landingSound = null return - temp_pod.landingSound = input(holder, "Please pick a sound file to play when the pod lands! I reccomend a nice \"oh shit, i'm sorry\", incase you hit someone with the pod.", "Pick a Sound File") as null|sound + var/soundInput = input(holder, "Please pick a sound file to play when the pod lands! I reccomend a nice \"oh shit, i'm sorry\", incase you hit someone with the pod.", "Pick a Sound File") as null|sound + if (isnull(soundInput)) + return + temp_pod.landingSound = soundInput . = TRUE if("openingSound") //Admin sound from a local file that plays when the pod opens if (!isnull(temp_pod.openingSound)) temp_pod.openingSound = null return - temp_pod.openingSound = input(holder, "Please pick a sound file to play when the pod opens! I reccomend a stock sound effect of kids cheering at a party, incase your pod is full of fun exciting stuff!", "Pick a Sound File") as null|sound + var/soundInput = input(holder, "Please pick a sound file to play when the pod opens! I reccomend a stock sound effect of kids cheering at a party, incase your pod is full of fun exciting stuff!", "Pick a Sound File") as null|sound + if (isnull(soundInput)) + return + temp_pod.openingSound = soundInput . = TRUE if("leavingSound") //Admin sound from a local file that plays when the pod leaves if (!isnull(temp_pod.leavingSound)) temp_pod.leavingSound = null return - temp_pod.leavingSound = input(holder, "Please pick a sound file to play when the pod leaves! I reccomend a nice slide whistle sound, especially if you're using the reverse pod effect.", "Pick a Sound File") as null|sound + var/soundInput = input(holder, "Please pick a sound file to play when the pod leaves! I reccomend a nice slide whistle sound, especially if you're using the reverse pod effect.", "Pick a Sound File") as null|sound + if (isnull(soundInput)) + return + temp_pod.leavingSound = soundInput . = TRUE if("soundVolume") //Admin sound from a local file that plays when the pod leaves - if (temp_pod.soundVolume != 50) - temp_pod.soundVolume = 50 + if (temp_pod.soundVolume != initial(temp_pod.soundVolume)) + temp_pod.soundVolume = initial(temp_pod.soundVolume) return - temp_pod.soundVolume = input(holder, "Please pick a volume. Default is between 1 and 100 with 50 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num - if (isnull(temp_pod.soundVolume)) - temp_pod.soundVolume = 50 + var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 80 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num + if (isnull(soundInput)) + return + temp_pod.soundVolume = soundInput . = TRUE ////////////////////////////STYLE CHANGES////////////////// //Style is a value that is used to keep track of what the pod is supposed to look like. It can be used with the POD_STYLES list (in cargo.dm defines) @@ -364,6 +413,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if("styleGondola") temp_pod.setStyle(STYLE_GONDOLA) . = TRUE + if("styleSeeThrough") + temp_pod.setStyle(STYLE_SEETHROUGH) + . = TRUE if("refresh") //Refresh the Pod bay. User should press this if they spawn something new in the centcom bay. Automatically called whenever the user launches a pod refreshBay() . = TRUE @@ -403,12 +455,17 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if(left_click) //When we left click: preLaunch() //Fill the acceptableTurfs list from the orderedArea list. Then, fill up the launchList list with items from the acceptableTurfs list based on the manner of launch (ordered, random, etc) if (!isnull(specificTarget)) - target = get_turf(specificTarget) //if we have a specific mob target, then always launch the pod at the turf of the mob + target = get_turf(specificTarget) //if we have a specific target, then always launch the pod at the turf of the target else if (target) target = get_turf(target) //Make sure we're aiming at a turf rather than an item or effect or something else return //if target is null and we don't have a specific target, cancel - + if (effectAnnounce) + deadchat_broadcast("A special package is being launched at the station!", turf_target = target) + var/list/bouttaDie = list() + for (var/mob/living/M in target) + bouttaDie.Add(M) + supplypod_punish_log(bouttaDie) if (!effectBurst) //If we're not using burst mode, just launch normally. launch(target) else @@ -422,7 +479,6 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm else launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire. - log_admin("Centcom Supplypod Launch: [key_name(user)] launched a supplypod in [AREACOORD(target)]") /datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay @@ -486,11 +542,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those for (var/atom/movable/O in launchList) DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod - new /obj/effect/DPtarget(A, toLaunch) //Create the DPTarget, which will eventually forceMove the temp_pod to it's location - else + new /obj/effect/abstract/DPtarget(A, toLaunch) //Create the DPTarget, which will eventually forceMove the temp_pod to it's location + else for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod - new /obj/effect/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location + new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location if (launchClone) launchCounter++ //We only need to increment launchCounter if we are cloning objects. //If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order @@ -508,4 +564,26 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm updateCursor(FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode qdel(temp_pod) //Delete the temp_pod qdel(selector) //Delete the selector effect - . = ..() \ No newline at end of file + . = ..() + +/datum/centcom_podlauncher/proc/supplypod_punish_log(var/list/whoDyin) + var/podString = effectBurst ? "5 pods" : "a pod" + var/whomString = "" + if (LAZYLEN(whoDyin)) + for (var/mob/living/M in whoDyin) + whomString += "[key_name(M)], " + + var/delayString = temp_pod.landingDelay == initial(temp_pod.landingDelay) ? "" : " Delay=[temp_pod.landingDelay*0.1]s" + var/damageString = temp_pod.damage == 0 ? "" : " Dmg=[temp_pod.damage]" + var/explosionString = "" + var/explosion_sum = temp_pod.explosionSize[1] + temp_pod.explosionSize[2] + temp_pod.explosionSize[3] + temp_pod.explosionSize[4] + if (explosion_sum != 0) + explosionString = " Boom=|" + for (var/X in temp_pod.explosionSize) + explosionString += "[X]|" + + var/msg = "launched [podString][whomString].[delayString][damageString][explosionString]]" + message_admins("[key_name_admin(usr)] [msg] in [AREACOORD(specificTarget)].") + if (!isemptylist(whoDyin)) + for (var/mob/living/M in whoDyin) + admin_ticket_log(M, "[key_name_admin(usr)] [msg]") diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index 642719c8..00c020e6 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -183,7 +183,7 @@ LZ = pick(empty_turfs) if (SO.pack.cost <= SSshuttle.points && LZ)//we need to call the cost check again because of the CHECK_TICK call SSshuttle.points -= SO.pack.cost - new /obj/effect/DPtarget(LZ, podType, SO) + new /obj/effect/abstract/DPtarget(LZ, podType, SO) . = TRUE update_icon() else @@ -200,7 +200,8 @@ for(var/i in 1 to MAX_EMAG_ROCKETS) var/LZ = pick(empty_turfs) LAZYREMOVE(empty_turfs, LZ) - new /obj/effect/DPtarget(LZ, podType, SO) + new /obj/effect/abstract/DPtarget(LZ, podType, SO) . = TRUE update_icon() CHECK_TICK + diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm index f36ff0ce..7b78facf 100644 --- a/code/modules/cargo/gondolapod.dm +++ b/code/modules/cargo/gondolapod.dm @@ -39,15 +39,22 @@ set name = "Release Contents" set category = "Gondola" set desc = "Release any contents stored within your vast belly." - linked_pod.open(src, manual = TRUE) + linked_pod.open(src, forced = TRUE) + +/mob/living/simple_animal/pet/gondola/gondolapod/examine(mob/user) + ..() + if (contents.len) + to_chat(user, "It looks like it hasn't made its delivery yet.") + else + to_chat(user, "It looks like it has already made its delivery.") /mob/living/simple_animal/pet/gondola/gondolapod/verb/check() set name = "Count Contents" set category = "Gondola" set desc = "Take a deep look inside youself, and count up what's inside" var/total = contents.len - if (total) - to_chat(src, "You detect [total] object[total > 1 ? "s" : ""] within your incredibly vast belly.") + if (total) + to_chat(src, "You detect [total] object\s within your incredibly vast belly.") else to_chat(src, "A closer look inside yourself reveals... nothing.") diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm index eba56379..0676e74e 100644 --- a/code/modules/cargo/packs/emergency.dm +++ b/code/modules/cargo/packs/emergency.dm @@ -12,7 +12,7 @@ /datum/supply_pack/emergency/vehicle name = "Biker Gang Kit" //TUNNEL SNAKES OWN THIS TOWN desc = "TUNNEL SNAKES OWN THIS TOWN. Contains an unbranded All Terrain Vehicle, and a complete gang outfit -- consists of black gloves, a menacing skull bandanna, and a SWEET leather overcoat!" - cost = 2500 + cost = 7500 contraband = TRUE contains = list(/obj/vehicle/ridden/atv, /obj/item/key, diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index f5e203cd..3269d92b 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -15,15 +15,17 @@ armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80) anchored = TRUE //So it cant slide around after landing anchorable = FALSE + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 //*****NOTE*****: Many of these comments are similarly described in centcom_podlauncher.dm. If you change them here, please consider doing so in the centcom podlauncher code as well! var/adminNamed = FALSE //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc) var/bluespace = FALSE //If true, the pod deletes (in a shower of sparks) after landing var/landingDelay = 30 //How long the pod takes to land after launching var/openingDelay = 30 //How long the pod takes to open after landing - var/departureDelay = 30 //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom) + var/departureDelay = 30 //How long the pod takes to leave after opening. If bluespace = TRUE, it deletes. If reversing = TRUE, it flies back to centcom. var/damage = 0 //Damage that occurs to any mob under the pod when it lands. var/effectStun = FALSE //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! var/effectLimb = FALSE //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands + var/effectOrgans = FALSE //If true, yeets out every limb and organ from anyone caught under the pod when it lands var/effectGib = FALSE //If true, anyone under the pod will be gibbed when it lands var/effectStealth = FALSE //If true, a target icon isnt displayed on the turf where the pod will land var/effectQuiet = FALSE //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) @@ -31,10 +33,13 @@ var/effectCircle = FALSE //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here var/style = STYLE_STANDARD //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod. var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom + var/fallDuration = 4 + var/fallingSoundLength = 11 + var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands var/landingSound //Admin sound to play when the pod lands var/openingSound //Admin sound to play when the pod opens var/leavingSound //Admin sound to play when the pod leaves - var/soundVolume = 50 //Volume to play sounds at. Ignores the cap + var/soundVolume = 80 //Volume to play sounds at. Ignores the cap var/bay //Used specifically for the centcom_podlauncher datum. Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map. var/list/explosionSize = list(0,0,2,3) @@ -48,16 +53,18 @@ style = STYLE_CENTCOM bluespace = TRUE explosionSize = list(0,0,0,0) - landingDelay = 5 //Very speedy! + landingDelay = 20 //Very speedy! resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF /obj/structure/closet/supplypod/Initialize() - ..() + . = ..() setStyle(style, TRUE) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly /obj/structure/closet/supplypod/update_icon() cut_overlays() - if (style != STYLE_INVISIBLE) //If we're invisible, we dont bother adding any overlays + if (style == STYLE_SEETHROUGH || style == STYLE_INVISIBLE) //If we're invisible, we dont bother adding any overlays + return + else if (opened) add_overlay("[icon_state]_open") else @@ -75,7 +82,7 @@ update_icon() /obj/structure/closet/supplypod/tool_interact(obj/item/W, mob/user) - if (bluespace) //We dont want to worry about interacting with bluespace pods, as they are due to delete themselves soon anyways. + if(bluespace) //We dont want to worry about interacting with bluespace pods, as they are due to delete themselves soon anyways. return FALSE else ..() @@ -86,9 +93,6 @@ /obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding. return -/obj/structure/closet/supplypod/prevent_content_explosion() //Useful for preventing epicenter explosions from damaging contents - return TRUE - /obj/structure/closet/supplypod/toggle(mob/living/user) //Supplypods shouldn't be able to be manually opened under any circumstances, as the open() proc generates supply order datums return @@ -101,17 +105,33 @@ if (effectLimb && iscarbon(M)) //If effectLimb is true (which means we pop limbs off when we hit people): var/mob/living/carbon/CM = M for (var/obj/item/bodypart/bodypart in CM.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands - if(bodypart.body_part != HEAD && bodypart.body_zone != CHEST)//we dont want to kill him, just teach em a lesson! + if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)//we dont want to kill him, just teach em a lesson! if (bodypart.dismemberable) bodypart.dismember() //Using the power of flextape i've sawed this man's limb in half! break + if (effectOrgans && iscarbon(M)) //effectOrgans means remove every organ in our mob + var/mob/living/carbon/CM = M + for(var/X in CM.internal_organs) + var/destination = get_edge_target_turf(T, pick(GLOB.alldirs)) //Pick a random direction to toss them in + var/obj/item/organ/O = X + O.Remove(CM) //Note that this isn't the same proc as for lists + O.forceMove(T) //Move the organ outta the body + O.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away + sleep(1) + for (var/obj/item/bodypart/bodypart in CM.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands + var/destination = get_edge_target_turf(T, pick(GLOB.alldirs)) + if (bodypart.dismemberable) + bodypart.dismember() //Using the power of flextape i've sawed this man's bodypart in half! + bodypart.throw_at(destination, 2, 3) + sleep(1) + if (effectGib) //effectGib is on, that means whatever's underneath us better be fucking oof'd on M.adjustBruteLoss(5000) //THATS A LOT OF DAMAGE (called just in case gib() doesnt work on em) M.gib() //After adjusting the fuck outta that brute loss we finish the job with some satisfying gibs - M.adjustBruteLoss(damage) - - - if (B[1] || B[2] || B[3] || B[4]) //If the explosion list isn't all zeroes, call an explosion + else + M.adjustBruteLoss(damage) + var/explosion_sum = B[1] + B[2] + B[3] + B[4] + if (explosion_sum != 0) //If the explosion list isn't all zeroes, call an explosion explosion(get_turf(src), B[1], B[2], B[3], flame_range = B[4], silent = effectQuiet, ignorecap = istype(src, /obj/structure/closet/supplypod/centcompod)) //less advanced equipment than bluespace pod, so larger explosion when landing else if (!effectQuiet) //If our explosion list IS all zeroes, we still make a nice explosion sound (unless the effectQuiet var is true) playsound(src, "explosion", landingSound ? 15 : 80, 1) @@ -128,22 +148,31 @@ /obj/structure/closet/supplypod/open(atom/movable/holder, var/broken = FALSE, var/forced = FALSE) //The holder var represents an atom whose contents we will be working with var/turf/T = get_turf(holder) //Get the turf of whoever's contents we're talking about + if (!holder) + return + if(opened) + return + opened = TRUE //This is to ensure we don't open something that has already been opened var/mob/M if (istype(holder, /mob)) //Allows mobs to assume the role of the holder, meaning we look at the mob's contents rather than the supplypod's contents. Typically by this point the supplypod's contents have already been moved over to the mob's contents M = holder if (M.key && !forced && !broken) //If we are player controlled, then we shouldnt open unless the opening is manual, or if it is due to being destroyed (represented by the "broken" parameter) return - opened = TRUE //This is to ensure we don't open something that has already been opened if (openingSound) - playsound(get_turf(holder), openingSound, soundVolume, 0, 0) + playsound(get_turf(holder), openingSound, soundVolume, 0, 0) //Special admin sound to play INVOKE_ASYNC(holder, .proc/setOpened) //Use the INVOKE_ASYNC proc to call setOpened() on whatever the holder may be, without giving the atom/movable base class a setOpened() proc definition + if (style == STYLE_SEETHROUGH) + update_icon() for (var/atom/movable/O in holder.contents) //Go through the contents of the holder O.forceMove(T) //move everything from the contents of the holder to the turf of the holder - if (!effectQuiet) //If we aren't being quiet, play an open sound + if (!effectQuiet && !openingSound && style != STYLE_SEETHROUGH) //If we aren't being quiet, play the default pod open sound playsound(get_turf(holder), open_sound, 15, 1, -3) if (broken) //If the pod is opening because it's been destroyed, we end here return - addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time + if (style == STYLE_SEETHROUGH) + depart(src) + else + addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time /obj/structure/closet/supplypod/proc/depart(atom/movable/holder) if (leavingSound) @@ -151,7 +180,7 @@ if (reversing) //If we're reversing, we call the close proc. This sends the pod back up to centcom close(holder) else if (bluespace) //If we're a bluespace pod, then delete ourselves (along with our holder, if a seperate holder exists) - if (style != STYLE_INVISIBLE) + if (!effectQuiet && style != STYLE_INVISIBLE && style != STYLE_SEETHROUGH) do_sparks(5, TRUE, holder) //Create some sparks right before closing qdel(src) //Delete ourselves and the holder if (holder != src) @@ -164,13 +193,14 @@ if (ismob(O) && !isliving(O)) //We dont want to take ghosts with us continue O.forceMove(holder) //Put objects inside before we close - var/obj/effect/temp_visual/risingPod = new /obj/effect/temp_visual/DPfall(get_turf(holder), src) //Make a nice animation of flying back up + var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot holder.forceMove(bay) //Move the pod back to centcom, where it belongs animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod + QDEL_IN(risingPod, 10) reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) + bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever open(holder, forced = TRUE) - return /obj/structure/closet/supplypod/proc/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open() proc for more details update_icon() @@ -179,12 +209,11 @@ update_icon() /obj/structure/closet/supplypod/Destroy() - if (!opened) //If we havent opened yet, we're opening because we've been destroyed. Lets dump our contents by opening up - open(src, broken = TRUE) - return ..() + open(src, broken = TRUE) //Lets dump our contents by opening up + . = ..() //------------------------------------FALLING SUPPLY POD-------------------------------------// -/obj/effect/temp_visual/DPfall //Falling pod +/obj/effect/abstract/DPfall //Falling pod name = "" icon = 'icons/obj/supplypods.dmi' pixel_x = -16 @@ -192,17 +221,22 @@ pixel_z = 200 desc = "Get out of the way!" layer = FLY_LAYER//that wasnt flying, that was falling with style! - randomdir = FALSE icon_state = "" -/obj/effect/temp_visual/DPfall/Initialize(dropLocation, obj/structure/closet/supplypod/pod) - if (pod.style != STYLE_INVISIBLE) //Check to ensure the pod isn't invisible +/obj/effect/abstract/DPfall/Initialize(dropLocation, obj/structure/closet/supplypod/pod) + if (pod.style == STYLE_SEETHROUGH) + pixel_x = -16 + pixel_y = 0 + for (var/atom/movable/O in pod.contents) + var/icon/I = getFlatIcon(O) //im so sorry + add_overlay(I) + else if (pod.style != STYLE_INVISIBLE) //Check to ensure the pod isn't invisible icon_state = "[pod.icon_state]_falling" name = pod.name . = ..() //------------------------------------TEMPORARY_VISUAL-------------------------------------// -/obj/effect/DPtarget //This is the object that forceMoves the supplypod to it's location +/obj/effect/abstract/DPtarget //This is the object that forceMoves the supplypod to it's location name = "Landing Zone Indicator" desc = "A holographic projection designating the landing zone of something. It's probably best to stand back." icon = 'icons/mob/actions/actions_items.dmi' @@ -212,45 +246,60 @@ var/obj/effect/temp_visual/fallingPod //Temporary "falling pod" that we animate var/obj/structure/closet/supplypod/pod //The supplyPod that will be landing ontop of this target -/obj/effect/ex_act() - return - -/obj/effect/DPtarget/Initialize(mapload, podParam, var/datum/supply_order/SO = null) +/obj/effect/abstract/DPtarget/Initialize(mapload, podParam, single_order = null) + . = ..() if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does) podParam = new podParam() //If its just a path, instantiate it pod = podParam - if (SO) - SO.generate(pod) - for (var/mob/living/M in podParam) //If there are any mobs in the supplypod, we want to forceMove them into the target. This is so that they can see where they are about to land, AND so that they don't get sent to the nullspace error room (as the pod is currently in nullspace) + if (single_order) + if (istype(single_order, /datum/supply_order)) + var/datum/supply_order/SO = single_order + SO.generate(pod) + else if (istype(single_order, /atom/movable)) + var/atom/movable/O = single_order + O.forceMove(pod) + for (var/mob/living/M in pod) //If there are any mobs in the supplypod, we want to forceMove them into the target. This is so that they can see where they are about to land, AND so that they don't get sent to the nullspace error room (as the pod is currently in nullspace) M.forceMove(src) if(pod.effectStun) //If effectStun is true, stun any mobs caught on this target until the pod gets a chance to hit them for (var/mob/living/M in get_turf(src)) M.Stun(pod.landingDelay+10, ignore_canstun = TRUE)//you aint goin nowhere, kid. if (pod.effectStealth) //If effectStealth is true we want to be invisible - alpha = 255 + icon_state = "" + if (pod.fallDuration == initial(pod.fallDuration) && pod.landingDelay + pod.fallDuration < pod.fallingSoundLength) + pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound + pod.fallingSound = 'sound/weapons/mortar_whistle.ogg' + var/soundStartTime = pod.landingDelay - pod.fallingSoundLength + pod.fallDuration + if (soundStartTime < 0) + soundStartTime = 1 + if (!pod.effectQuiet) + addtimer(CALLBACK(src, .proc/playFallingSound), soundStartTime) addtimer(CALLBACK(src, .proc/beginLaunch, pod.effectCircle), pod.landingDelay) -/obj/effect/DPtarget/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle - fallingPod = new /obj/effect/temp_visual/DPfall(drop_location(), pod) +/obj/effect/abstract/DPtarget/proc/playFallingSound() + playsound(src, pod.fallingSound, pod.soundVolume, 1, 6) + +/obj/effect/abstract/DPtarget/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle + fallingPod = new /obj/effect/abstract/DPfall(drop_location(), pod) var/matrix/M = matrix(fallingPod.transform) //Create a new matrix that we can rotate var/angle = effectCircle ? rand(0,360) : rand(70,110) //The angle that we can come in from - fallingPod.pixel_x = cos(angle)*200 //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the target - fallingPod.pixel_z = sin(angle)*200 + fallingPod.pixel_x = cos(angle)*400 //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the target + fallingPod.pixel_z = sin(angle)*400 var/rotation = Get_Pixel_Angle(fallingPod.pixel_z, fallingPod.pixel_x) //CUSTOM HOMEBREWED proc that is just arctan with extra steps M.Turn(rotation) //Turn our matrix accordingly fallingPod.transform = M //Transform the animated pod according to the matrix M = matrix(pod.transform) //Make another matrix based on the pod M.Turn(rotation) //Turn the matrix pod.transform = M //Turn the actual pod (Won't be visible until endLaunch() proc tho) - animate(fallingPod, pixel_z = 0, pixel_x = -16, time = 3, , easing = LINEAR_EASING) //Make the pod fall! At an angle! - addtimer(CALLBACK(src, .proc/endLaunch), 3, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation + animate(fallingPod, pixel_z = 0, pixel_x = -16, time = pod.fallDuration, , easing = LINEAR_EASING) //Make the pod fall! At an angle! + addtimer(CALLBACK(src, .proc/endLaunch), pod.fallDuration, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation -/obj/effect/DPtarget/proc/endLaunch() +/obj/effect/abstract/DPtarget/proc/endLaunch() + pod.update_icon() pod.forceMove(drop_location()) //The fallingPod animation is over, now's a good time to forceMove the actual pod into position + QDEL_NULL(fallingPod) //Delete the falling pod effect, because at this point its animation is over. We dont use temp_visual because we want to manually delete it as soon as the pod appears for (var/mob/living/M in src) //Remember earlier (initialization) when we moved mobs into the DPTarget so they wouldnt get lost in nullspace? Time to get them out M.forceMove(pod) pod.preOpen() //Begin supplypod open procedures. Here effects like explosions, damage, and other dangerous (and potentially admin-caused, if the centcom_podlauncher datum was used) memes will take place - QDEL_NULL(fallingPod) //The fallingPod's (the animated effect, not the actual pod) purpose is complete. It can rest easy now qdel(src) //ditto //------------------------------------UPGRADES-------------------------------------// @@ -261,3 +310,4 @@ icon_state = "cargodisk" item_state = "card-id" w_class = WEIGHT_CLASS_SMALL + diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 2a5bc386..114f2416 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -240,6 +240,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/bgstate_options = list("000", "midgrey", "hiro", "FFF", "white", "steel", "techmaint", "dark", "plating", "reinforced") var/show_mismatched_markings = FALSE //determines whether or not the markings lists should show markings that don't match the currently selected species. Intentionally left unsaved. + var/hide_ckey = FALSE //pref for hiding if your ckey shows round-end or not /datum/preferences/New(client/C) parent = C @@ -368,6 +369,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "[medical_records]" else dat += "[TextPreview(medical_records)]...
" + dat += "
Hide ckey: [hide_ckey ? "Enabled" : "Disabled"]
" dat += "" //Character Appearance @@ -1689,6 +1691,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) msg = msg features["flavor_text"] = msg + if("hide_ckey") + hide_ckey = !hide_ckey + if(user) + user.mind?.hide_ckey = hide_ckey + if("hair") var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null if(new_hair) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 9ad29dee..1b33e6cb 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -337,7 +337,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["feature_human_ears"] >> features["ears"] S["feature_deco_wings"] >> features["deco_wings"] - + S["hide_ckey"] >> hide_ckey //saved per-character //Custom names @@ -591,6 +591,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Record Flavor Text WRITE_FILE(S["security_records"] , security_records) WRITE_FILE(S["medical_records"] , medical_records) + //hide c-key + WRITE_FILE(S["hide_ckey"] , hide_ckey) //Quirks WRITE_FILE(S["all_quirks"] , all_quirks) diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 6c67cb3d..619f981d 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -48,6 +48,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 pull_force = INFINITY density = TRUE anchored = TRUE + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 var/mob/living/wizard var/z_original = 0 var/destination @@ -103,9 +104,6 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 /obj/effect/immovablerod/ex_act(severity, target) return 0 -/obj/structure/closet/supplypod/prevent_content_explosion() - return TRUE - /obj/effect/immovablerod/singularity_act() return @@ -167,3 +165,4 @@ obj/effect/immovablerod/attack_hand(mob/living/user) new /obj/structure/festivus/anchored(drop_location()) new /obj/effect/anomaly/flux(drop_location()) qdel(src) + diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index d47543e0..48c87cae 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -21,7 +21,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( /datum/hallucination/delusion = 2, /datum/hallucination/shock = 1, /datum/hallucination/death = 1, - /datum/hallucination/oh_yeah = 1 + /datum/hallucination/oh_yeah = 1, + /datum/hallucination/sleeping_carp = 1 )) @@ -890,7 +891,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( SEND_SOUND(target, 'sound/ai/aimalf.ogg') if("meteors") //Meteors inbound! to_chat(target, "

Meteor Alert

") - to_chat(target, "

Meteors have been detected on collision course with the station.

") + to_chat(target, "

Meteors have been detected on collision course with the station. Estimated time until impact: [round(rand(300,600)/60)] minutes.

") SEND_SOUND(target, 'sound/ai/meteors.ogg') if("supermatter") SEND_SOUND(target, 'sound/magic/charge.ogg') @@ -1311,3 +1312,26 @@ GLOBAL_LIST_INIT(hallucination_list, list( H.preparePixelProjectile(target, start) H.fire() qdel(src) + +/datum/hallucination/sleeping_carp + +/datum/hallucination/sleeping_carp/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + var/list/mobsyup + for (var/mob/living/carbon/A in orange(C,1)) + if (get_dist(C,A) < 2) + LAZYADD(mobsyup,A) + if (!LAZYLEN(mobsyup)) + qdel(src) + return + var/mob/living/carbon/G = pick(mobsyup) + if (prob(50)) + C.visible_message("[C] falls to the ground screaming and clutching [C.p_their()] wrist!", \ + "[G] grabs your wrist and violently wrenches it to the side!") + C.emote("scream") + C.dropItemToGround(C.get_active_held_item()) + C.Knockdown(60) + else + to_chat(C,"[G] violently grabs you!") + qdel(src) diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index aaa9a03f..fb40e4ee 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -173,7 +173,7 @@ for(var/V in categories) categories[V] = list() for(var/V in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[V] + var/datum/design/D = SSresearch.techweb_design_by_id(V) for(var/C in categories) if(C in D.category) categories[C] += D @@ -185,11 +185,11 @@ dat += "
" for(var/V in categories[cat]) var/datum/design/D = V - dat += "[D.name]: Make" + dat += "[D.name]: Make" if(cat in timesFiveCategories) - dat += "x5" + dat += "x5" if(ispath(D.build_path, /obj/item/stack)) - dat += "x10" + dat += "x10" dat += "([D.materials[MAT_BIOMASS]/efficiency])
" dat += "
" else @@ -210,9 +210,9 @@ var/S = 0 for(var/obj/item/reagent_containers/food/snacks/grown/I in contents) S += 5 - if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1) + if(I.reagents.get_reagent_amount("nutriment") < 0.1) points += 1*productivity - else points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)*10*productivity + else points += I.reagents.get_reagent_amount("nutriment")*10*productivity qdel(I) if(S) processing = TRUE @@ -306,8 +306,20 @@ var/amount = (text2num(href_list["amount"])) //Can't be outside these (if you change this keep a sane limit) amount = CLAMP(amount, 1, 50) - var/datum/design/D = locate(href_list["create"]) - create_product(D, amount) + var/id = href_list["create"] + if(!stored_research.researched_designs.Find(id)) + //naughty naughty + stack_trace("ID did not map to a researched datum [id]") + return + + //Get design by id (or may return error design) + var/datum/design/D = SSresearch.techweb_design_by_id(id) + //Valid design datum, amount and the datum is not the error design, lets proceed + if(D && amount && !istype(D, /datum/design/error_design)) + create_product(D, amount) + //This shouldnt happen normally but href forgery is real + else + stack_trace("ID could not be turned into a valid techweb design datum [id]") updateUsrDialog() else if(href_list["menu"]) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 4dc2e248..f5150b47 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -121,7 +121,7 @@ dat += "Smelt Alloys
" for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) dat += "[D.name] " if (selected_alloy == D.id) dat += " Smelting" diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index e37e8416..34262af1 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -209,7 +209,7 @@ data["alloys"] = list() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D))) if (!mat_container) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 1b4aff7f..08a80459 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -13,6 +13,7 @@ mob_biotypes = list(MOB_SILICON) rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE speech_span = SPAN_ROBOT + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1 var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS var/last_lawchange_announce = 0 @@ -70,9 +71,6 @@ /mob/living/silicon/contents_explosion(severity, target) return -/mob/living/silicon/prevent_content_explosion() - return TRUE - /mob/living/silicon/proc/cancelAlarm() return @@ -411,3 +409,4 @@ /mob/living/silicon/handle_high_gravity(gravity) return + diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index b45ead9d..876feef8 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -37,6 +37,10 @@ icon_state = "mouse_[body_color]" icon_living = "mouse_[body_color]" icon_dead = "mouse_[body_color]_dead" + if(name == "mouse") // Faster than checking for mobtypes, just checks if this mouse is a generic mouse. + if(prob(2)) //2% chance to turn a generic mouse into a boommouse + new /mob/living/simple_animal/mouse/boommouse(src.loc) + qdel(src) /mob/living/simple_animal/mouse/proc/splat() src.health = 0 @@ -121,3 +125,28 @@ /obj/item/reagent_containers/food/snacks/deadmouse/on_grind() reagents.clear_reagents() +/mob/living/simple_animal/mouse/boommouse + name = "boommouse" //obviously inspired on rimworld + desc = "A mutated rat with a pack of... Plasma on its back? I wouldn't really touch it if I were you." + icon = 'hyperstation/icons/mob/animal.dmi' + icon_state = "mouse_brown" + icon_living = "mouse_brown" + icon_dead = "mouse_brown_dead" + see_in_dark = 12 + maxHealth = 7 + health = 7 + chew_probability = 0 + +/mob/living/simple_animal/mouse/boommouse/Initialize() + . = ..() + //Force icons because mouse/initialize randomizes them + icon = 'hyperstation/icons/mob/animal.dmi' + icon_state = "mouse_plasma" + icon_living = "mouse_plasma" + icon_dead = "mouse_plasma" //No need for a dead sprite since it qdels itself on death + +/mob/living/simple_animal/mouse/boommouse/death(gibbed, toast) + visible_message("The boommouse violently explodes!") + atmos_spawn_air("plasma=15;TEMP=500") + explosion(src.loc, 0, 1, 2, 0, 1, 0, 2, 0, 0) + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 9bea6c5d..874b980e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -27,6 +27,7 @@ mob_size = MOB_SIZE_LARGE layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1 var/list/crusher_loot var/medal_type var/score_type = BOSS_SCORE @@ -44,9 +45,6 @@ QDEL_NULL(internal) . = ..() -/mob/living/simple_animal/hostile/megafauna/prevent_content_explosion() - return TRUE - /mob/living/simple_animal/hostile/megafauna/death(gibbed) if(health > 0) return @@ -121,9 +119,10 @@ recovery_time = world.time + buffer_time /mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill) - if(!medal_type || (flags_1 & ADMIN_SPAWNED_1) || !SSmedals.hub_enabled) //Don't award medals if the medal type isn't set + if(!medal_type || (flags_1 & ADMIN_SPAWNED_1)) //Don't award medals if the medal type isn't set return FALSE - + if(!SSmedals.hub_enabled) // This allows subtypes to carry on other special rewards not tied with medals. (such as bubblegum's arena shuttle) + return TRUE for(var/mob/living/L in view(7,src)) if(L.stat || !L.client) continue diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 4c39d51d..e3af2080 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -52,4 +52,5 @@ var/datum/callback/CB = foo CB.Invoke() + mind?.hide_ckey = client?.prefs?.hide_ckey log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP) diff --git a/code/modules/ninja/suit/shoes.dm b/code/modules/ninja/suit/shoes.dm index 115b14b6..c77190a9 100644 --- a/code/modules/ninja/suit/shoes.dm +++ b/code/modules/ninja/suit/shoes.dm @@ -2,7 +2,7 @@ name = "ninja shoes" desc = "A pair of running shoes. Excellent for running and even better for smashing skulls." icon_state = "s-ninja" - item_state = "secshoes" + item_state = "s-ninja" permeability_coefficient = 0.01 clothing_flags = NOSLIP resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 8f416cc0..cc70253d 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -78,6 +78,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) icon_state = "darkmatter" density = TRUE anchored = TRUE + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 var/uid = 1 var/static/gl_uid = 1 light_range = 4 @@ -728,9 +729,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/contents_explosion(severity, target) return -/obj/machinery/power/supermatter_crystal/prevent_content_explosion() - return TRUE - /obj/machinery/power/supermatter_crystal/engine is_main_engine = TRUE diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 83eb4126..9a32c834 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -607,6 +607,17 @@ color = "#F0FFF0" metabolization_rate = 0.5 * REAGENTS_METABOLISM toxpwr = 0 + overdose_threshold = 40 + +/datum/reagent/toxin/lipolicide/overdose_process(mob/living/carbon/C) + . = ..() + if(current_cycle >=41 && prob(15)) + C.spew_organ() + C.vomit(10, TRUE, TRUE, 4) + to_chat(C, "You feel something lumpy come up as you vomit.") + if(prob(1)) + to_chat(C, "You feel like your organs are on fire!") + C.IgniteMob() /datum/reagent/toxin/lipolicide/on_mob_life(mob/living/carbon/M) if(M.nutrition <= NUTRITION_LEVEL_STARVING) diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index d039fc5b..2e4f3e60 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -79,12 +79,12 @@ /obj/machinery/disposal/attackby(obj/item/I, mob/user, params) add_fingerprint(user) if(!pressure_charging && !full_pressure && !flush) - if(istype(I, /obj/item/screwdriver)) + if(I.tool_behaviour == TOOL_SCREWDRIVER) panel_open = !panel_open I.play_tool_sound(src) to_chat(user, "You [panel_open ? "remove":"attach"] the screws around the power connection.") return - else if(istype(I, /obj/item/weldingtool) && panel_open) + else if(I.tool_behaviour == TOOL_WELDER && panel_open) if(!I.tool_start_check(user, amount=0)) return @@ -99,7 +99,7 @@ return place_item_in_disposal(I, user) update_icon() - return 1 //no afterattack + return TRUE //no afterattack else return ..() @@ -283,32 +283,16 @@ var/datum/oracle_ui/themed/nano/ui obj_flags = CAN_BE_HIT | USES_TGUI | SHOVABLE_ONTO - // attack by item places it in to disposal /obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - if(!pressure_charging && !full_pressure && !flush) - if(I.tool_behaviour == TOOL_SCREWDRIVER) - panel_open = !panel_open - I.play_tool_sound(src) - to_chat(user, "You [panel_open ? "remove":"attach"] the screws around the power connection.") - return - else if(I.tool_behaviour == TOOL_WELDER && panel_open) - if(!I.tool_start_check(user, amount=0)) - return - - to_chat(user, "You start slicing the floorweld off \the [src]...") - if(I.use_tool(src, user, 20, volume=100) && panel_open) - to_chat(user, "You slice the floorweld off \the [src].") - deconstruct() - return - - if(user.a_intent != INTENT_HARM) - if((I.item_flags & ABSTRACT) || !user.temporarilyRemoveItemFromInventory(I)) - return - place_item_in_disposal(I, user) + if(istype(I, /obj/item/storage/bag/trash)) //Not doing component overrides because this is a specific type. + var/obj/item/storage/bag/trash/T = I + var/datum/component/storage/STR = T.GetComponent(/datum/component/storage) + to_chat(user, "You empty the bag.") + for(var/obj/item/O in T.contents) + STR.remove_from_storage(O,src) + T.update_icon() update_icon() - return TRUE //no afterattack else return ..() diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index a376807e..516ae5bd 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -27,6 +27,7 @@ other types of metals and chemistry for reagents). */ //DESIGNS ARE GLOBAL. DO NOT CREATE OR DESTROY THEM AT RUNTIME OUTSIDE OF INIT, JUST REFERENCE THEM TO WHATEVER YOU'RE DOING! //why are you yelling? +//DO NOT REFERENCE OUTSIDE OF SSRESEARCH. USE THE PROCS IN SSRESEARCH TO OBTAIN A REFERENCE. /datum/design //Datum for object designs, used in construction var/name = "Name" //Name of the created object. @@ -48,8 +49,12 @@ other types of metals and chemistry for reagents). var/research_icon_state var/icon_cache +/datum/design/error_design + name = "ERROR" + desc = "This usually means something in the database has corrupted. If this doesn't go away automatically, inform Central Command so their techs can fix this ASAP(tm)" + /datum/design/Destroy() - CRASH("DESIGN DATUMS SHOULD NOT EVER BE DESTROYED AS THEY ARE ONLY MEANT TO BE IN A GLOBAL LIST AND REFERENCED FOR US.") + SSresearch.techweb_designs -= id return ..() /datum/design/proc/icon_html(client/user) diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 92fb26a6..7dd7b760 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -102,7 +102,7 @@ Note: Must be placed within 3 tiles of the R&D Console return FALSE if (id && id != RESEARCH_MATERIAL_RECLAMATION_ID) - var/datum/techweb_node/TN = get_techweb_node_by_id(id) + var/datum/techweb_node/TN = SSresearch.techweb_node_by_id(id) if(!istype(TN)) return FALSE var/dpath = loaded_item.type @@ -123,7 +123,7 @@ Note: Must be placed within 3 tiles of the R&D Console return FALSE SSblackbox.record_feedback("nested tally", "item_deconstructed", 1, list("[TN.id]", "[loaded_item.type]")) if(destroy_item(loaded_item)) - linked_console.stored_research.boost_with_path(SSresearch.techweb_nodes[TN.id], dpath) + linked_console.stored_research.boost_with_path(SSresearch.techweb_node_by_id(TN.id), dpath) else var/list/point_value = techweb_item_point_check(loaded_item) diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index a8d61311..357405d1 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -128,13 +128,13 @@ var/list/res = list("Already researched:") var/list/boosted = list("Already boosted:") for(var/node_id in listin) - var/datum/techweb_node/N = get_techweb_node_by_id(node_id) + var/datum/techweb_node/N = SSresearch.techweb_node_by_id(node_id) var/str = "[N.display_name]: [listin[N]] points." - if(SSresearch.science_tech.researched_nodes[N]) + if(SSresearch.science_tech.researched_nodes[N.id]) res += str - else if(SSresearch.science_tech.boosted_nodes[N]) + else if(SSresearch.science_tech.boosted_nodes[N.id]) boosted += str - if(SSresearch.science_tech.visible_nodes[N]) //JOY OF DISCOVERY! + if(SSresearch.science_tech.visible_nodes[N.id]) //JOY OF DISCOVERY! output += str output += boosted + res dat += output @@ -182,10 +182,10 @@ experiment(dotype,process) use_power(750) if(dotype != FAIL) - var/list/datum/techweb_node/nodes = techweb_item_boost_check(process) + var/list/nodes = techweb_item_boost_check(process) var/picked = pickweight(nodes) //This should work. if(linked_console) - linked_console.stored_research.boost_with_path(picked, process.type) + linked_console.stored_research.boost_with_path(SSresearch.techweb_node_by_id(picked), process.type) updateUsrDialog() /obj/machinery/rnd/experimentor/proc/matchReaction(matching,reaction) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index 2a1eb23f..0978986f 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -36,7 +36,7 @@ /obj/machinery/rnd/production/proc/update_designs() cached_designs.Cut() for(var/i in stored_research.researched_designs) - var/datum/design/d = stored_research.researched_designs[i] + var/datum/design/d = SSresearch.techweb_design_by_id(i) if((isnull(allowed_department_flags) || (d.departmental_flags & allowed_department_flags)) && (d.build_type & allowed_buildtypes)) cached_designs |= d @@ -113,7 +113,7 @@ amount = text2num(amount) if(isnull(amount)) amount = 1 - var/datum/design/D = (linked_console || requires_console)? linked_console.stored_research.researched_designs[id] : get_techweb_design_by_id(id) + var/datum/design/D = (linked_console || requires_console)? (linked_console.stored_research.researched_designs[id]? SSresearch.techweb_design_by_id(id) : null) : SSresearch.techweb_design_by_id(id) if(!istype(D)) return FALSE if(!(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) @@ -160,7 +160,7 @@ /obj/machinery/rnd/production/proc/search(string) matching_designs.Cut() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(D.build_type & allowed_buildtypes) || !(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) continue if(findtext(D.name,string)) @@ -336,7 +336,7 @@ l += "

Browsing [selected_category]:

" var/coeff = efficiency_coeff for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category)|| !(D.build_type & allowed_buildtypes)) continue if(!(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) diff --git a/code/modules/research/nanites/nanite_program_hub.dm b/code/modules/research/nanites/nanite_program_hub.dm index e33f3d7c..d6e0bbda 100644 --- a/code/modules/research/nanites/nanite_program_hub.dm +++ b/code/modules/research/nanites/nanite_program_hub.dm @@ -69,7 +69,7 @@ if(current_category != "Main") var/list/program_list = list() for(var/i in linked_techweb.researched_designs) - var/datum/design/nanites/D = linked_techweb.researched_designs[i] + var/datum/design/nanites/D = SSresearch.techweb_design_by_id(i) if(!istype(D)) continue if(current_category in D.category) @@ -116,9 +116,9 @@ disk.program = null disk.name = initial(disk.name) . = TRUE - - + + /obj/machinery/nanite_program_hub/admin/Initialize() . = ..() linked_techweb = SSresearch.admin_tech - + diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index f0bea36c..26c21150 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -36,10 +36,10 @@ Nothing else in the console has ID requirements. var/locked = FALSE var/tdisk_uple = FALSE var/ddisk_uple = FALSE - var/datum/techweb_node/selected_node - var/datum/design/selected_design + var/datum/selected_node_id + var/datum/selected_design_id var/selected_category - var/list/datum/design/matching_designs + var/list/matching_design_ids var/disk_slot_selected var/searchstring = "" var/searchtype = "" @@ -88,7 +88,7 @@ Nothing else in the console has ID requirements. . = ..() stored_research = SSresearch.science_tech stored_research.consoles_accessing[src] = TRUE - matching_designs = list() + matching_design_ids = list() SyncRDevices() /obj/machinery/computer/rdconsole/Destroy() @@ -109,9 +109,7 @@ Nothing else in the console has ID requirements. if(d_disk) d_disk.forceMove(get_turf(src)) d_disk = null - matching_designs = null - selected_node = null - selected_design = null + matching_design_ids = null return ..() /obj/machinery/computer/rdconsole/attackby(obj/item/D, mob/user, params) @@ -144,7 +142,7 @@ Nothing else in the console has ID requirements. if(!stored_research.available_nodes[id] || stored_research.researched_nodes[id]) say("Node unlock failed: Either already researched or not available!") return FALSE - var/datum/techweb_node/TN = SSresearch.techweb_nodes[id] + var/datum/techweb_node/TN = SSresearch.techweb_node_by_id(id) if(!istype(TN)) say("Node unlock failed: Unknown error.") return FALSE @@ -153,7 +151,7 @@ Nothing else in the console has ID requirements. investigate_log("[key_name(user)] researched [id]([json_encode(price)]) on techweb id [stored_research.id].", INVESTIGATE_RESEARCH) if(stored_research == SSresearch.science_tech) SSblackbox.record_feedback("associative", "science_techweb_unlock", 1, list("id" = "[id]", "name" = TN.display_name, "price" = "[json_encode(price)]", "time" = SQLtime())) - if(stored_research.research_node(SSresearch.techweb_nodes[id])) + if(stored_research.research_node_id(id)) say("Successfully researched [TN.display_name].") var/logname = "Unknown" if(isAI(user)) @@ -280,7 +278,7 @@ Nothing else in the console has ID requirements. l += ui_protolathe_header() l += "

Browsing [selected_category]:

" for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category)|| !(D.build_type & PROTOLATHE)) continue if(!(isnull(linked_lathe.allowed_department_flags) || (D.departmental_flags & linked_lathe.allowed_department_flags))) @@ -335,7 +333,8 @@ Nothing else in the console has ID requirements. RDSCREEN_UI_LATHE_CHECK var/list/l = list() l += ui_protolathe_header() - for(var/datum/design/D in matching_designs) + for(var/id in matching_design_ids) + var/datum/design/D = SSresearch.techweb_design_by_id(id) if(!(isnull(linked_lathe.allowed_department_flags) || (D.departmental_flags & linked_lathe.allowed_department_flags))) continue var/temp_material @@ -431,7 +430,7 @@ Nothing else in the console has ID requirements. l += "

Browsing [selected_category]:

" for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category) || !(D.build_type & IMPRINTER)) continue if(!(isnull(linked_imprinter.allowed_department_flags) || (D.departmental_flags & linked_imprinter.allowed_department_flags))) @@ -464,7 +463,8 @@ Nothing else in the console has ID requirements. l += ui_circuit_header() l += "

Search results:

" - for(var/datum/design/D in matching_designs) + for(var/id in matching_design_ids) + var/datum/design/D = SSresearch.techweb_design_by_id(id) if(!(isnull(linked_imprinter.allowed_department_flags) || (D.departmental_flags & linked_imprinter.allowed_department_flags))) continue var/temp_materials @@ -524,7 +524,7 @@ Nothing else in the console has ID requirements. l += "Load Technology to Disk
" l += "

Stored Technology Nodes:

" for(var/i in t_disk.stored_research.researched_nodes) - var/datum/techweb_node/N = t_disk.stored_research.researched_nodes[i] + var/datum/techweb_node/N = SSresearch.techweb_node_by_id(i) l += "[N.display_name]" l += "
" return l @@ -550,7 +550,7 @@ Nothing else in the console has ID requirements. l += "Return to Disk Operations
" l += "

Load Design to Disk:

" for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) l += "[D.name] " l += "Copy to Disk" l += "
" @@ -571,7 +571,7 @@ Nothing else in the console has ID requirements. for(var/id in boostable_nodes) anything = TRUE var/list/worth = boostable_nodes[id] - var/datum/techweb_node/N = get_techweb_node_by_id(id) + var/datum/techweb_node/N = SSresearch.techweb_node_by_id(id) l += "
[RDSCREEN_NOBREAK]" if (stored_research.researched_nodes[N.id]) // already researched @@ -635,32 +635,33 @@ Nothing else in the console has ID requirements. var/list/columns = list() var/max_tier = 0 for (var/node_ in stored_research.tiers) - var/datum/techweb_node/node = node_ - var/tier = stored_research.tiers[node] + var/datum/techweb_node/node = SSresearch.techweb_node_by_id(node_) + var/tier = stored_research.tiers[node.id] LAZYINITLIST(columns["[tier]"]) // String hackery to make the numbers associative columns["[tier]"] += ui_techweb_single_node(node, minimal=(tier != 1)) max_tier = max(max_tier, tier) l += "[RDSCREEN_NOBREAK]" - for(var/tier in 0 to max_tier) - l += "[RDSCREEN_NOBREAK]" + if(max_tier) + for(var/tier in 0 to max_tier) + l += "[RDSCREEN_NOBREAK]" l += "
ResearchedAvailableFuture
[RDSCREEN_NOBREAK]" - l += columns["[tier]"] - l += "[RDSCREEN_NOBREAK]" + l += columns["[tier]"] + l += "
[RDSCREEN_NOBREAK]" else var/list/avail = list() //This could probably be optimized a bit later. var/list/unavail = list() var/list/res = list() for(var/v in stored_research.researched_nodes) - res += stored_research.researched_nodes[v] + res += SSresearch.techweb_node_by_id(v) for(var/v in stored_research.available_nodes) if(stored_research.researched_nodes[v]) continue - avail += stored_research.available_nodes[v] + avail += SSresearch.techweb_node_by_id(v) for(var/v in stored_research.visible_nodes) if(stored_research.available_nodes[v]) continue - unavail += stored_research.visible_nodes[v] + unavail += SSresearch.techweb_node_by_id(v) l += "

Technology Nodes:

[RDSCREEN_NOBREAK]" l += "

Available for Research:

" for(var/datum/techweb_node/N in avail) @@ -702,13 +703,14 @@ Nothing else in the console has ID requirements. l += "
[node.price_display(stored_research)]" // red - missing prereqs if(ui_mode == RDCONSOLE_UI_MODE_NORMAL) l += "[node.description]" - for(var/i in node.designs) - var/datum/design/D = node.designs[i] + for(var/i in node.design_ids) + var/datum/design/D = SSresearch.techweb_design_by_id(i) l += "[D.icon_html(usr)][RDSCREEN_NOBREAK]" l += "
[RDSCREEN_NOBREAK]" return l /obj/machinery/computer/rdconsole/proc/ui_techweb_nodeview() + var/datum/techweb_node/selected_node = SSresearch.techweb_node_by_id(selected_node_id) RDSCREEN_UI_SNODE_CHECK var/list/l = list() if(stored_research.hidden_nodes[selected_node.id]) @@ -716,65 +718,65 @@ Nothing else in the console has ID requirements. return l += "[RDSCREEN_NOBREAK]" - if (length(selected_node.prerequisites)) + if (length(selected_node.prereq_ids)) l += "[RDSCREEN_NOBREAK]" l += "[RDSCREEN_NOBREAK]" - if (length(selected_node.unlocks)) + if (length(selected_node.unlock_ids)) l += "[RDSCREEN_NOBREAK]" l += "[RDSCREEN_NOBREAK]" - if (length(selected_node.prerequisites)) + if (length(selected_node.unlock_ids)) l += "[RDSCREEN_NOBREAK]" l += "[RDSCREEN_NOBREAK]" - if (length(selected_node.unlocks)) + if (length(selected_node.unlock_ids)) l += "[RDSCREEN_NOBREAK]" l += "
RequiresCurrent NodeUnlocks
[RDSCREEN_NOBREAK]" - for (var/i in selected_node.prerequisites) - l += ui_techweb_single_node(selected_node.prerequisites[i]) + for (var/i in selected_node.prereq_ids) + l += ui_techweb_single_node(SSresearch.techweb_node_by_id(i)) l += "[RDSCREEN_NOBREAK]" l += ui_techweb_single_node(selected_node, selflink=FALSE) l += "[RDSCREEN_NOBREAK]" - for (var/i in selected_node.unlocks) - l += ui_techweb_single_node(selected_node.unlocks[i]) + for (var/i in selected_node.unlock_ids) + l += ui_techweb_single_node(SSresearch.techweb_node_by_id(i)) l += "
[RDSCREEN_NOBREAK]" return l /obj/machinery/computer/rdconsole/proc/ui_techweb_designview() //Legacy code + var/datum/design/selected_design = SSresearch.techweb_design_by_id(selected_design_id) RDSCREEN_UI_SDESIGN_CHECK var/list/l = list() - var/datum/design/D = selected_design - l += "
[D.icon_html(usr)][D.name]
[RDSCREEN_NOBREAK]" - if(D.build_type) + l += "
[selected_design.icon_html(usr)][selected_design.name]
[RDSCREEN_NOBREAK]" + if(selected_design.build_type) var/lathes = list() - if(D.build_type & IMPRINTER) + if(selected_design.build_type & IMPRINTER) lathes += "[machine_icon(/obj/machinery/rnd/production/circuit_imprinter)][RDSCREEN_NOBREAK]" - if (linked_imprinter && D.id in stored_research.researched_designs) - l += "Imprint" - if(D.build_type & PROTOLATHE) + if (linked_imprinter && stored_research.researched_designs[selected_design.id]) + l += "Imprint" + if(selected_design.build_type & PROTOLATHE) lathes += "[machine_icon(/obj/machinery/rnd/production/protolathe)][RDSCREEN_NOBREAK]" - if (linked_lathe && D.id in stored_research.researched_designs) - l += "Construct" - if(D.build_type & AUTOLATHE) + if (linked_lathe && stored_research.researched_designs[selected_design.id]) + l += "Construct" + if(selected_design.build_type & AUTOLATHE) lathes += "[machine_icon(/obj/machinery/autolathe)][RDSCREEN_NOBREAK]" - if(D.build_type & MECHFAB) + if(selected_design.build_type & MECHFAB) lathes += "[machine_icon(/obj/machinery/mecha_part_fabricator)][RDSCREEN_NOBREAK]" - if(D.build_type & BIOGENERATOR) + if(selected_design.build_type & BIOGENERATOR) lathes += "[machine_icon(/obj/machinery/biogenerator)][RDSCREEN_NOBREAK]" - if(D.build_type & LIMBGROWER) + if(selected_design.build_type & LIMBGROWER) lathes += "[machine_icon(/obj/machinery/limbgrower)][RDSCREEN_NOBREAK]" - if(D.build_type & SMELTER) + if(selected_design.build_type & SMELTER) lathes += "[machine_icon(/obj/machinery/mineral/processing_unit)][RDSCREEN_NOBREAK]" l += "Construction types:" l += lathes l += "" l += "Required materials:" - var/all_mats = D.materials + D.reagents_list + var/all_mats = selected_design.materials + selected_design.reagents_list for(var/M in all_mats) l += "* [CallMaterialName(M)] x [all_mats[M]]" l += "Unlocked by:" - for (var/node in D.unlocked_by) - l += ui_techweb_single_node(node) + for (var/i in selected_design.unlocked_by) + l += ui_techweb_single_node(SSresearch.techweb_node_by_id(i)) l += "[RDSCREEN_NOBREAK]
" return l @@ -965,7 +967,7 @@ Nothing else in the console has ID requirements. qdel(t_disk.stored_research) t_disk.stored_research = new say("Wiping technology disk.") - if(ls["copy_tech"]) //Copy some technology la from the research holder to the disk. + if(ls["copy_tech"]) //Copy some technology from the research holder to the disk. if(QDELETED(t_disk)) say("No Technology Disk Inserted!") return @@ -999,12 +1001,12 @@ Nothing else in the console has ID requirements. return say("Uploading technology disk.") t_disk.stored_research.copy_research_to(stored_research) - if(ls["copy_design"]) //Copy design la from the research holder to the design disk. + if(ls["copy_design"]) //Copy design from the research holder to the design disk. if(QDELETED(d_disk)) say("No Design Disk Inserted!") return var/slot = text2num(ls["copy_design"]) - var/datum/design/D = stored_research.researched_designs[ls["copy_design_ID"]] + var/datum/design/D = SSresearch.techweb_design_by_id(ls["copy_design_ID"]) if(D) var/autolathe_friendly = TRUE if(D.reagents_list.len) @@ -1031,12 +1033,12 @@ Nothing else in the console has ID requirements. linked_destroy.unload_item() screen = RDSCREEN_MENU if(ls["view_node"]) - selected_node = SSresearch.techweb_nodes[ls["view_node"]] + selected_node_id = ls["view_node"] screen = RDSCREEN_TECHWEB_NODEVIEW if(ls["view_design"]) - selected_design = SSresearch.techweb_designs[ls["view_design"]] + selected_design_id = ls["view_design"] screen = RDSCREEN_TECHWEB_DESIGNVIEW - if(ls["updt_design"]) //Uples the research holder with design la from the design disk. + if(ls["updt_design"]) //Uploads a design from disk to the techweb. if(QDELETED(d_disk)) say("No design disk found.") return @@ -1044,9 +1046,9 @@ Nothing else in the console has ID requirements. if(!n) for(var/D in d_disk.blueprints) if(D) - stored_research.add_design(D) + stored_research.add_design(D, TRUE) else - stored_research.add_design(d_disk.blueprints[n]) + stored_research.add_design(d_disk.blueprints[n], TRUE) updateUsrDialog() @@ -1075,17 +1077,17 @@ Nothing else in the console has ID requirements. /obj/machinery/computer/rdconsole/proc/rescan_views() var/compare - matching_designs.Cut() + matching_design_ids.Cut() if(searchtype == "proto") compare = PROTOLATHE else if(searchtype == "imprint") compare = IMPRINTER for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(D.build_type & compare)) continue if(findtext(D.name,searchstring)) - matching_designs.Add(D) + matching_design_ids.Add(D.id) /obj/machinery/computer/rdconsole/proc/check_canprint(datum/design/D, buildtype) var/amount = 50 diff --git a/code/modules/research/techweb/__techweb_helpers.dm b/code/modules/research/techweb/__techweb_helpers.dm index 3c420a91..2fd89378 100644 --- a/code/modules/research/techweb/__techweb_helpers.dm +++ b/code/modules/research/techweb/__techweb_helpers.dm @@ -1,48 +1,3 @@ - -/proc/initialize_all_techweb_nodes(clearall = FALSE) - if(islist(SSresearch.techweb_nodes) && clearall) - QDEL_LIST(SSresearch.techweb_nodes) - if(islist(SSresearch.techweb_nodes_starting && clearall)) - QDEL_LIST(SSresearch.techweb_nodes_starting) - var/list/returned = list() - for(var/path in subtypesof(/datum/techweb_node)) - var/datum/techweb_node/TN = path - if(isnull(initial(TN.id))) - continue - TN = new path - if(returned[initial(TN.id)]) - stack_trace("WARNING: Techweb node ID clash with ID [initial(TN.id)] detected!") - SSresearch.errored_datums[TN] = initial(TN.id) - continue - returned[initial(TN.id)] = TN - if(TN.starting_node) - SSresearch.techweb_nodes_starting[TN.id] = TN - SSresearch.techweb_nodes = returned - verify_techweb_nodes() //Verify all nodes have ids and such. - calculate_techweb_nodes() - calculate_techweb_boost_list() - verify_techweb_nodes() //Verify nodes and designs have been crosslinked properly. - -/proc/initialize_all_techweb_designs(clearall = FALSE) - if(islist(SSresearch.techweb_designs) && clearall) - QDEL_LIST(SSresearch.techweb_designs) - var/list/returned = list() - for(var/path in subtypesof(/datum/design)) - var/datum/design/DN = path - if(isnull(initial(DN.id))) - stack_trace("WARNING: Design with null ID detected. Build path: [initial(DN.build_path)]") - continue - else if(initial(DN.id) == DESIGN_ID_IGNORE) - continue - DN = new path - if(returned[initial(DN.id)]) - stack_trace("WARNING: Design ID clash with ID [initial(DN.id)] detected!") - SSresearch.errored_datums[DN] = initial(DN.id) - continue - returned[initial(DN.id)] = DN - SSresearch.techweb_designs = returned - verify_techweb_designs() - /proc/count_unique_techweb_nodes() var/static/list/L = typesof(/datum/techweb_node) return L.len @@ -51,135 +6,10 @@ var/static/list/L = typesof(/datum/design) return L.len -/proc/get_techweb_node_by_id(id) - if(SSresearch.techweb_nodes[id]) - return SSresearch.techweb_nodes[id] - -/proc/get_techweb_design_by_id(id) - if(SSresearch.techweb_designs[id]) - return SSresearch.techweb_designs[id] - -/proc/research_node_id_error(id) - if(SSresearch.invalid_node_ids[id]) - SSresearch.invalid_node_ids[id]++ - else - SSresearch.invalid_node_ids[id] = 1 - -/proc/design_id_error(id) - if(SSresearch.invalid_design_ids[id]) - SSresearch.invalid_design_ids[id]++ - else - SSresearch.invalid_design_ids[id] = 1 - /proc/node_boost_error(id, message) WARNING("Invalid boost information for node \[[id]\]: [message]") SSresearch.invalid_node_boost[id] = message -/proc/verify_techweb_nodes() - for(var/n in SSresearch.techweb_nodes) - var/datum/techweb_node/N = SSresearch.techweb_nodes[n] - if(!istype(N)) - WARNING("Invalid research node with ID [n] detected and removed.") - SSresearch.techweb_nodes -= n - research_node_id_error(n) - for(var/p in N.prereq_ids) - var/datum/techweb_node/P = SSresearch.techweb_nodes[p] - if(!istype(P)) - WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.") - N.prereq_ids -= p - research_node_id_error(p) - for(var/d in N.design_ids) - var/datum/design/D = SSresearch.techweb_designs[d] - if(!istype(D)) - WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.") - N.designs -= d - design_id_error(d) - for(var/p in N.prerequisites) - var/datum/techweb_node/P = N.prerequisites[p] - if(!istype(P)) - WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.") - N.prerequisites -= p - research_node_id_error(p) - for(var/u in N.unlocks) - var/datum/techweb_node/U = N.unlocks[u] - if(!istype(U)) - WARNING("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.") - N.unlocks -= u - research_node_id_error(u) - for(var/d in N.designs) - var/datum/design/D = N.designs[d] - if(!istype(D)) - WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.") - N.designs -= d - design_id_error(d) - for(var/p in N.boost_item_paths) - if(!ispath(p)) - N.boost_item_paths -= p - node_boost_error(N.id, "[p] is not a valid path.") - var/list/points = N.boost_item_paths[p] - if(islist(points)) - for(var/i in points) - if(!isnum(points[i])) - node_boost_error(N.id, "[points[i]] is not a valid number.") - else if(!SSresearch.point_types[i]) - node_boost_error(N.id, "[i] is not a valid point type.") - else if(!isnull(points)) - N.boost_item_paths -= p - node_boost_error(N.id, "No valid list.") - CHECK_TICK - -/proc/verify_techweb_designs() - for(var/d in SSresearch.techweb_designs) - var/datum/design/D = SSresearch.techweb_designs[d] - if(!istype(D)) - stack_trace("WARNING: Invalid research design with ID [d] detected and removed.") - SSresearch.techweb_designs -= d - CHECK_TICK - -/proc/calculate_techweb_nodes() - for(var/design_id in SSresearch.techweb_designs) - var/datum/design/D = SSresearch.techweb_designs[design_id] - D.unlocked_by.Cut() - for(var/node_id in SSresearch.techweb_nodes) - var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] - node.prerequisites = list() - node.unlocks = list() - node.designs = list() - for(var/i in node.prereq_ids) - node.prerequisites[i] = SSresearch.techweb_nodes[i] - for(var/i in node.design_ids) - var/datum/design/D = SSresearch.techweb_designs[i] - node.designs[i] = D - D.unlocked_by += node - if(node.hidden) - SSresearch.techweb_nodes_hidden[node.id] = node - CHECK_TICK - generate_techweb_unlock_linking() - -/proc/generate_techweb_unlock_linking() - for(var/node_id in SSresearch.techweb_nodes) //Clear all unlock links to avoid duplication. - var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] - node.unlocks = list() - for(var/node_id in SSresearch.techweb_nodes) - var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] - for(var/prereq_id in node.prerequisites) - var/datum/techweb_node/prereq_node = node.prerequisites[prereq_id] - prereq_node.unlocks[node.id] = node - -/proc/calculate_techweb_boost_list(clearall = FALSE) - if(clearall) - SSresearch.techweb_boost_items = list() - for(var/node_id in SSresearch.techweb_nodes) - var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] - for(var/path in node.boost_item_paths) - if(!ispath(path)) - continue - if(length(SSresearch.techweb_boost_items[path])) - SSresearch.techweb_boost_items[path][node.id] = node.boost_item_paths[path] - else - SSresearch.techweb_boost_items[path] = list(node.id = node.boost_item_paths[path]) - CHECK_TICK - /proc/techweb_item_boost_check(obj/item/I) //Returns an associative list of techweb node datums with values of the boost it gives. var/list/returned = list() if(SSresearch.techweb_boost_items[I.type]) return SSresearch.techweb_boost_items[I.type] //It should already be formatted in node datum = list(point type = value) diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm index 855c741e..41f99948 100644 --- a/code/modules/research/techweb/_techweb.dm +++ b/code/modules/research/techweb/_techweb.dm @@ -5,12 +5,13 @@ //Techweb datums are meant to store unlocked research, being able to be stored on research consoles, servers, and disks. They are NOT global. /datum/techweb - var/list/datum/techweb_node/researched_nodes = list() //Already unlocked and all designs are now available. Assoc list, id = datum - var/list/datum/techweb_node/visible_nodes = list() //Visible nodes, doesn't mean it can be researched. Assoc list, id = datum - var/list/datum/techweb_node/available_nodes = list() //Nodes that can immediately be researched, all reqs met. assoc list, id = datum - var/list/datum/design/researched_designs = list() //Designs that are available for use. Assoc list, id = datum - var/list/datum/techweb_node/boosted_nodes = list() //Already boosted nodes that can't be boosted again. node datum = path of boost object. - var/list/datum/techweb_node/hidden_nodes = list() //Hidden nodes. id = datum. Used for unhiding nodes when requirements are met by removing the entry of the node. + var/list/researched_nodes = list() //Already unlocked and all designs are now available. Assoc list, id = TRUE + var/list/visible_nodes = list() //Visible nodes, doesn't mean it can be researched. Assoc list, id = TRUE + var/list/available_nodes = list() //Nodes that can immediately be researched, all reqs met. assoc list, id = TRUE + var/list/researched_designs = list() //Designs that are available for use. Assoc list, id = TRUE + var/list/custom_designs = list() //Custom inserted designs like from disks that should survive recalculation. + var/list/boosted_nodes = list() //Already boosted nodes that can't be boosted again. node id = path of boost object. + var/list/hidden_nodes = list() var/list/deconstructed_items = list() //items already deconstructed for a generic point boost. path = list(point_type = points) var/list/research_points = list() //Available research points. type = number var/list/obj/machinery/computer/rdconsole/consoles_accessing = list() @@ -20,13 +21,13 @@ var/organization = "Third-Party" //Organization name, used for display. var/list/last_bitcoins = list() //Current per-second production, used for display only. var/list/discovered_mutations = list() //Mutations discovered by genetics, this way they are shared and cant be destroyed by destroying a single console - var/list/tiers = list() //Assoc list, datum = number, 1 is available, 2 is all reqs are 1, so on + var/list/tiers = list() //Assoc list, id = number, 1 is available, 2 is all reqs are 1, so on /datum/techweb/New() for(var/i in SSresearch.techweb_nodes_starting) - var/datum/techweb_node/DN = SSresearch.techweb_nodes_starting[i] + var/datum/techweb_node/DN = SSresearch.techweb_node_by_id(i) research_node(DN, TRUE, FALSE) - hidden_nodes = SSresearch.techweb_nodes_hidden + hidden_nodes = SSresearch.techweb_nodes_hidden.Copy() return ..() /datum/techweb/admin @@ -59,23 +60,24 @@ researched_designs = null available_nodes = null visible_nodes = null + custom_designs = null + SSresearch.techwebs -= src return ..() -/datum/techweb/proc/recalculate_nodes(recalculate_designs = FALSE) +/datum/techweb/proc/recalculate_nodes(recalculate_designs = FALSE, wipe_custom_designs = FALSE) var/list/datum/techweb_node/processing = list() - for(var/i in researched_nodes) - processing[i] = researched_nodes[i] - for(var/i in visible_nodes) - processing[i] = visible_nodes[i] - for(var/i in available_nodes) - processing[i] = available_nodes[i] - for(var/i in processing) - update_node_status(processing[i]) - if(recalculate_designs) //Wipes custom added designs like from design disks or anything like that! - researched_designs = list() - for(var/i in processing) - var/datum/techweb_node/TN = processing[i] - update_node_status(TN, FALSE) + for(var/id in researched_nodes) + processing[id] = TRUE + for(var/id in visible_nodes) + processing[id] = TRUE + for(var/id in available_nodes) + processing[id] = TRUE + if(recalculate_designs) + researched_designs = custom_designs.Copy() + if(wipe_custom_designs) + custom_designs = list() + for(var/id in processing) + update_node_status(SSresearch.techweb_node_by_id(id), FALSE) CHECK_TICK for(var/v in consoles_accessing) var/obj/machinery/computer/rdconsole/V = v @@ -165,21 +167,27 @@ research_points[type] = max(0, research_points[type] - amount) return TRUE -/datum/techweb/proc/add_design_by_id(id) - return add_design(get_techweb_design_by_id(id)) +/datum/techweb/proc/add_design_by_id(id, custom = FALSE) + return add_design(SSresearch.techweb_design_by_id(id), custom) -/datum/techweb/proc/add_design(datum/design/design) +/datum/techweb/proc/add_design(datum/design/design, custom = FALSE) if(!istype(design)) return FALSE researched_designs[design.id] = design + researched_designs[design.id] = TRUE + if(custom) + custom_designs[design.id] = TRUE return TRUE -/datum/techweb/proc/remove_design_by_id(id) - return remove_design(get_techweb_design_by_id(id)) +/datum/techweb/proc/remove_design_by_id(id, custom = FALSE) + return remove_design(SSresearch.techweb_design_by_id(id), custom) -/datum/techweb/proc/remove_design(datum/design/design) +/datum/techweb/proc/remove_design(datum/design/design, custom = FALSE) if(!istype(design)) return FALSE + if(custom_designs[design.id] && !custom) + return FALSE + custom_designs -= design.id researched_designs -= design.id return TRUE @@ -193,7 +201,7 @@ return techweb_point_display_generic(research_points) /datum/techweb/proc/research_node_id(id, force, auto_update_points) - return research_node(get_techweb_node_by_id(id), force, auto_update_points) + return research_node(SSresearch.techweb_node_by_id(id), force, auto_update_points) /datum/techweb/proc/research_node(datum/techweb_node/node, force = FALSE, auto_adjust_cost = TRUE) if(!istype(node)) @@ -204,17 +212,17 @@ return FALSE if(auto_adjust_cost) remove_point_list(node.get_price(src)) - researched_nodes[node.id] = node //Add to our researched list - for(var/i in node.unlocks) - visible_nodes[i] = node.unlocks[i] - update_node_status(node.unlocks[i]) - for(var/i in node.designs) - add_design(node.designs[i]) + researched_nodes[node.id] = TRUE //Add to our researched list + for(var/id in node.unlock_ids) + visible_nodes[id] = TRUE + update_node_status(SSresearch.techweb_node_by_id(id)) + for(var/id in node.design_ids) + add_design_by_id(id) update_node_status(node) return TRUE /datum/techweb/proc/unresearch_node_id(id) - return unresearch_node(get_techweb_node_by_id(id)) + return unresearch_node(SSresearch.techweb_node_by_id(id)) /datum/techweb/proc/unresearch_node(datum/techweb_node/node) if(!istype(node)) @@ -225,9 +233,9 @@ /datum/techweb/proc/boost_with_path(datum/techweb_node/N, itempath) if(!istype(N) || !ispath(itempath)) return FALSE - LAZYINITLIST(boosted_nodes[N]) + LAZYINITLIST(boosted_nodes[N.id]) for(var/i in N.boost_item_paths[itempath]) - boosted_nodes[N][i] = max(boosted_nodes[N][i], N.boost_item_paths[itempath][i]) + boosted_nodes[N.id][i] = max(boosted_nodes[N.id][i], N.boost_item_paths[itempath][i]) if(N.autounlock_by_boost) hidden_nodes -= N.id update_node_status(N) @@ -242,13 +250,13 @@ var/tier = 0 if (!researched_nodes[node.id]) // researched is tier 0 for (var/id in node.prereq_ids) - var/prereq_tier = tiers[node.prerequisites[id]] + var/prereq_tier = tiers[id] tier = max(tier, prereq_tier + 1) - if (tier != tiers[node]) - tiers[node] = tier - for (var/id in node.unlocks) - next += node.unlocks[id] + if (tier != tiers[node.id]) + tiers[node.id] = tier + for (var/id in node.unlock_ids) + next += SSresearch.techweb_node_by_id(id) current = next /datum/techweb/proc/update_node_status(datum/techweb_node/node, autoupdate_consoles = TRUE) @@ -258,8 +266,8 @@ if(researched_nodes[node.id]) researched = TRUE var/needed = node.prereq_ids.len - for(var/i in node.prereq_ids) - if(researched_nodes[i]) + for(var/id in node.prereq_ids) + if(researched_nodes[id]) visible = TRUE needed-- if(!needed) @@ -270,15 +278,15 @@ if(hidden_nodes[node.id]) //Hidden. return if(researched) - researched_nodes[node.id] = node - for(var/i in node.designs) - add_design(node.designs[i]) + researched_nodes[node.id] = TRUE + for(var/id in node.design_ids) + add_design(SSresearch.techweb_design_by_id(id)) else if(available) - available_nodes[node.id] = node + available_nodes[node.id] = TRUE else if(visible) - visible_nodes[node.id] = node + visible_nodes[node.id] = TRUE update_tiers(node) if(autoupdate_consoles) for(var/v in consoles_accessing) @@ -289,35 +297,34 @@ //Laggy procs to do specific checks, just in case. Don't use them if you can just use the vars that already store all this! /datum/techweb/proc/designHasReqs(datum/design/D) for(var/i in researched_nodes) - var/datum/techweb_node/N = researched_nodes[i] - for(var/I in N.designs) - if(D == N.designs[I]) - return TRUE + var/datum/techweb_node/N = SSresearch.techweb_node_by_id(i) + if(N.design_ids[D.id]) + return TRUE return FALSE /datum/techweb/proc/isDesignResearched(datum/design/D) return isDesignResearchedID(D.id) /datum/techweb/proc/isDesignResearchedID(id) - return researched_designs[id] + return researched_designs[id]? SSresearch.techweb_design_by_id(id) : FALSE /datum/techweb/proc/isNodeResearched(datum/techweb_node/N) return isNodeResearchedID(N.id) /datum/techweb/proc/isNodeResearchedID(id) - return researched_nodes[id] + return researched_nodes[id]? SSresearch.techweb_design_by_id(id) : FALSE /datum/techweb/proc/isNodeVisible(datum/techweb_node/N) return isNodeResearchedID(N.id) /datum/techweb/proc/isNodeVisibleID(id) - return visible_nodes[id] + return visible_nodes[id]? SSresearch.techweb_design_by_id(id) : FALSE /datum/techweb/proc/isNodeAvailable(datum/techweb_node/N) return isNodeAvailableID(N.id) /datum/techweb/proc/isNodeAvailableID(id) - return available_nodes[id] + return available_nodes[id]? SSresearch.techweb_design_by_id(id) : FALSE /datum/techweb/specialized var/allowed_buildtypes = ALL @@ -340,11 +347,11 @@ for(var/id in node_autounlock_ids) research_node_id(id, TRUE, FALSE) for(var/id in SSresearch.techweb_designs) - var/datum/design/D = SSresearch.techweb_designs[id] + var/datum/design/D = SSresearch.techweb_design_by_id(id) if(D.build_type & design_autounlock_buildtypes) for(var/i in D.category) if(i in design_autounlock_categories) - add_design(D) + add_design_by_id(D.id) break /datum/techweb/specialized/autounlocking/autolathe @@ -368,4 +375,4 @@ /datum/techweb/specialized/autounlocking/autobottler design_autounlock_buildtypes = AUTOBOTTLER - allowed_buildtypes = AUTOBOTTLER + allowed_buildtypes = AUTOBOTTLER diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index 6f075ce8..3935dbac 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -9,20 +9,85 @@ var/starting_node = FALSE //Whether it's available without any research. var/list/prereq_ids = list() var/list/design_ids = list() - var/list/datum/techweb_node/prerequisites = list() //Assoc list id = datum - var/list/datum/techweb_node/unlocks = list() //CALCULATED FROM OTHER NODE'S PREREQUISITES. Assoc list id = datum. - var/list/datum/design/designs = list() //Assoc list id = datum + var/list/unlock_ids = list() var/list/boost_item_paths = list() //Associative list, path = list(point type = point_value). var/autounlock_by_boost = TRUE //boosting this will autounlock this node. var/export_price = 0 //Cargo export price. - var/list/research_costs = 0 //Point cost to research. type = amount + var/list/research_costs = list() //Point cost to research. type = amount var/category = "Misc" //Category + +/datum/techweb_node/error_node + id = "ERROR" + display_name = "ERROR" + description = "This usually means something in the database has corrupted. If it doesn't go away automatically, inform Central Command for their techs to fix it ASAP(tm)" + +/datum/techweb_node/proc/Initialize() + //Make lists associative for lookup + for(var/id in prereq_ids) + prereq_ids[id] = TRUE + for(var/id in design_ids) + design_ids[id] = TRUE + for(var/id in unlock_ids) + unlock_ids[id] = TRUE + +/datum/techweb_node/Destroy() + SSresearch.techweb_nodes -= id + return ..() + +/datum/techweb_node/serialize_list(list/options) + . = list() + VARSET_TO_LIST(., id) + VARSET_TO_LIST(., display_name) + VARSET_TO_LIST(., hidden) + VARSET_TO_LIST(., starting_node) + VARSET_TO_LIST(., assoc_list_strip_value(prereq_ids)) + VARSET_TO_LIST(., assoc_list_strip_value(design_ids)) + VARSET_TO_LIST(., assoc_list_strip_value(unlock_ids)) + VARSET_TO_LIST(., boost_item_paths) + VARSET_TO_LIST(., autounlock_by_boost) + VARSET_TO_LIST(., export_price) + VARSET_TO_LIST(., research_costs) + VARSET_TO_LIST(., category) + +/datum/techweb_node/deserialize_list(list/input, list/options) + if(!input["id"]) + return + VARSET_FROM_LIST(input, id) + VARSET_FROM_LIST(input, display_name) + VARSET_FROM_LIST(input, hidden) + VARSET_FROM_LIST(input, starting_node) + VARSET_FROM_LIST(input, prereq_ids) + VARSET_FROM_LIST(input, design_ids) + VARSET_FROM_LIST(input, unlock_ids) + VARSET_FROM_LIST(input, boost_item_paths) + VARSET_FROM_LIST(input, autounlock_by_boost) + VARSET_FROM_LIST(input, export_price) + VARSET_FROM_LIST(input, research_costs) + VARSET_FROM_LIST(input, category) + Initialize() + return src + +/datum/techweb_node/proc/on_design_deletion(datum/design/D) + prune_design_id(D.id) + +/datum/techweb_node/proc/on_node_deletion(datum/techweb_node/TN) + prune_node_id(TN.id) + +/datum/techweb_node/proc/prune_design_id(design_id) + design_ids -= design_id + +/datum/techweb_node/proc/prune_node_id(node_id) + prereq_ids -= node_id + unlock_ids -= node_id + + + /datum/techweb_node/proc/get_price(datum/techweb/host) if(host) var/list/actual_costs = research_costs - if(host.boosted_nodes[src]) - var/list/L = host.boosted_nodes[src] + if(host.boosted_nodes[id]) + var/list/L = host.boosted_nodes[id] for(var/i in L) if(actual_costs[i]) actual_costs[i] -= L[i] diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 0a518774..e3acdea9 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -255,10 +255,21 @@ display_name = "Games and Toys" description = "For the slackers on the station." prereq_ids = list("comptech") - design_ids = list("arcade_battle", "arcade_orion", "slotmachine", "autoylathe") + design_ids = list("arcade_battle", "arcade_orion", "arcade_minesweeper", "slotmachine", "autoylathe") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) export_price = 5000 + +///datum/techweb_node/computer_board_gaming +// id = "computer_board_gaming" +// display_name = "Arcade Games" +// description = "For the slackers on the station." +// prereq_ids = list("comptech") +// design_ids = list("arcade_battle", "arcade_orion", "slotmachine") // Magic money +// research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) +// export_price = 2000 + + /////////////////////////Bluespace tech///////////////////////// /datum/techweb_node/bluespace_basic //Bluespace-memery id = "bluespace_basic" @@ -485,15 +496,6 @@ "portadrive_basic", "portadrive_advanced", "portadrive_super", "cardslot", "aislot", "miniprinter", "APClink", "bat_control", "bat_normal", "bat_advanced", "bat_super", "bat_micro", "bat_nano", "cpu_normal", "pcpu_normal", "cpu_small", "pcpu_small") -/datum/techweb_node/computer_board_gaming - id = "computer_board_gaming" - display_name = "Arcade Games" - description = "For the slackers on the station." - prereq_ids = list("comptech") - design_ids = list("arcade_battle", "arcade_orion", "slotmachine") // Magic money - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) - export_price = 2000 - /datum/techweb_node/comp_recordkeeping id = "comp_recordkeeping" display_name = "Computerized Recordkeeping" diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 2fb8c70f..9f670ecb 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -29,7 +29,8 @@ icon_state = "retractor_a" /obj/item/retractor/advanced/examine(mob/living/user) - to_chat(user, " It resembles a [tool_behaviour == TOOL_RETRACTOR ? "retractor" : "hemostat"]. " /obj/item/retractor/augment name = "retractor" @@ -131,7 +132,8 @@ icon_state = "surgicaldrill_a" /obj/item/surgicaldrill/advanced/examine(mob/living/user) - to_chat(user, "") + . = ..() + . += "" /obj/item/surgicaldrill/augment name = "surgical drill" @@ -203,7 +205,8 @@ icon_state = "scalpel_a" /obj/item/scalpel/advanced/examine(mob/living/user) - to_chat(user, "") + . = ..() + . += "" /obj/item/scalpel/augment name = "scalpel" diff --git a/html/create_object.html b/html/create_object.html index 3609e469..461dd373 100644 --- a/html/create_object.html +++ b/html/create_object.html @@ -40,6 +40,7 @@ Where: @@ -105,4 +106,4 @@ - \ No newline at end of file + diff --git a/hyperstation/icons/mob/animal.dmi b/hyperstation/icons/mob/animal.dmi new file mode 100644 index 00000000..6180b8e3 Binary files /dev/null and b/hyperstation/icons/mob/animal.dmi differ diff --git a/modular_citadel/code/game/machinery/toylathe.dm b/modular_citadel/code/game/machinery/toylathe.dm index aa8b8cd1..05388c3f 100644 --- a/modular_citadel/code/game/machinery/toylathe.dm +++ b/modular_citadel/code/game/machinery/toylathe.dm @@ -169,7 +169,7 @@ matching_designs.Cut() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(findtext(D.name,href_list["to_search"])) matching_designs.Add(D) updateUsrDialog() @@ -244,7 +244,7 @@ dat += materials_printout() for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] + var/datum/design/D = SSresearch.techweb_design_by_id(v) if(!(selected_category in D.category)) continue diff --git a/sound/music/thesettler.ogg b/sound/music/thesettler.ogg new file mode 100644 index 00000000..83caaee9 Binary files /dev/null and b/sound/music/thesettler.ogg differ diff --git a/sound/music/thewizard.ogg b/sound/music/thewizard.ogg new file mode 100644 index 00000000..614ceeaa Binary files /dev/null and b/sound/music/thewizard.ogg differ diff --git a/sound/weapons/mortar_long_whistle.ogg b/sound/weapons/mortar_long_whistle.ogg new file mode 100644 index 00000000..646d37d8 Binary files /dev/null and b/sound/weapons/mortar_long_whistle.ogg differ diff --git a/sound/weapons/mortar_whistle.ogg b/sound/weapons/mortar_whistle.ogg new file mode 100644 index 00000000..2d7e19d8 Binary files /dev/null and b/sound/weapons/mortar_whistle.ogg differ diff --git a/strings/round_start_sounds.txt b/strings/round_start_sounds.txt index 488b6152..30b829df 100644 --- a/strings/round_start_sounds.txt +++ b/strings/round_start_sounds.txt @@ -25,3 +25,5 @@ sound/music/goodbyemoonmen.ogg sound/music/flytothemoon_otomatone.ogg sound/music/milkyway.ogg sound/music/rocketridersprayer.ogg +sound/music/thewizard.ogg +sound/music/thesettler.ogg diff --git a/tgstation.dme b/tgstation.dme index c163de3d..e29c8f05 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -941,19 +941,25 @@ #include "code\game\objects\items\implants\implant_abductor.dm" #include "code\game\objects\items\implants\implant_chem.dm" #include "code\game\objects\items\implants\implant_clown.dm" +#include "code\game\objects\items\implants\implant_CQC.dm" #include "code\game\objects\items\implants\implant_exile.dm" #include "code\game\objects\items\implants\implant_explosive.dm" #include "code\game\objects\items\implants\implant_freedom.dm" #include "code\game\objects\items\implants\implant_krav_maga.dm" #include "code\game\objects\items\implants\implant_mindshield.dm" #include "code\game\objects\items\implants\implant_misc.dm" +#include "code\game\objects\items\implants\implant_mushpunch.dm" +#include "code\game\objects\items\implants\implant_plasmafist.dm" +#include "code\game\objects\items\implants\implant_psychobrawl.dm" #include "code\game\objects\items\implants\implant_radio.dm" #include "code\game\objects\items\implants\implant_slave.dm" +#include "code\game\objects\items\implants\implant_sleepingcarp.dm" #include "code\game\objects\items\implants\implant_spell.dm" #include "code\game\objects\items\implants\implant_stealth.dm" #include "code\game\objects\items\implants\implant_storage.dm" #include "code\game\objects\items\implants\implant_track.dm" #include "code\game\objects\items\implants\implant_uplink.dm" +#include "code\game\objects\items\implants\implant_wrestling.dm" #include "code\game\objects\items\implants\implantcase.dm" #include "code\game\objects\items\implants\implantchair.dm" #include "code\game\objects\items\implants\implanter.dm" diff --git a/tgui/assets/tgui.js b/tgui/assets/tgui.js index 88702292..3f5f7974 100644 --- a/tgui/assets/tgui.js +++ b/tgui/assets/tgui.js @@ -1 +1 @@ -require=function a(o,s,p){function u(e,t){if(!s[e]){if(!o[e]){var n="function"==typeof require&&require;if(!t&&n)return n(e,!0);if(c)return c(e,!0);var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}var i=s[e]={exports:{}};o[e][0].call(i.exports,function(t){return u(o[e][1][t]||t)},i,i.exports,a,o,s,p)}return s[e].exports}for(var c="function"==typeof require&&require,t=0;to;)i.call(t,a=r[o++])&&e.push(a);return e}},{78:78,81:81,82:82}],34:[function(t,e,n){var m=t(41),g=t(24),b=t(43),v=t(92),y=t(26),_="prototype",x=function(t,e,n){var a,r,i,o,s=t&x.F,p=t&x.G,u=t&x.S,c=t&x.P,l=t&x.B,d=p?m:u?m[e]||(m[e]={}):(m[e]||{})[_],f=p?g:g[e]||(g[e]={}),h=f[_]||(f[_]={});for(a in p&&(n=e),n)i=((r=!s&&d&&d[a]!==undefined)?d:n)[a],o=l&&r?y(i,m):c&&"function"==typeof i?y(Function.call,i):i,d&&v(d,a,i,t&x.U),f[a]!=i&&b(f,a,o),c&&h[a]!=i&&(h[a]=i)};m.core=g,x.F=1,x.G=2,x.S=4,x.P=8,x.B=16,x.W=32,x.U=64,x.R=128,e.exports=x},{24:24,26:26,41:41,43:43,92:92}],35:[function(t,e,n){var r=t(127)("match");e.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[r]=!1,!"/./"[t](e)}catch(a){}}return!0}},{127:127}],36:[function(t,e,n){e.exports=function(t){try{return!!t()}catch(e){return!0}}},{}],37:[function(t,e,n){"use strict";var s=t(43),p=t(92),u=t(36),c=t(29),l=t(127);e.exports=function(e,t,n){var a=l(e),r=n(c,a,""[e]),i=r[0],o=r[1];u(function(){var t={};return t[a]=function(){return 7},7!=""[e](t)})&&(p(String.prototype,e,i),s(RegExp.prototype,a,2==t?function(t,e){return o.call(t,this,e)}:function(t){return o.call(t,this)}))}},{127:127,29:29,36:36,43:43,92:92}],38:[function(t,e,n){"use strict";var a=t(8);e.exports=function(){var t=a(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},{8:8}],39:[function(t,e,n){"use strict";var f=t(50),h=t(52),m=t(116),g=t(26),b=t(127)("isConcatSpreadable");e.exports=function v(t,e,n,a,r,i,o,s){for(var p,u,c=r,l=0,d=!!o&&g(o,s,3);ldocument.F=Object<\/script>"),t.close(),c=t.F;n--;)delete c[u][s[n]];return c()};t.exports=Object.create||function(t,e){var n;return null!==t?(r[u]=i(t),n=new r,r[u]=null,n[p]=t):n=c(),e===undefined?n:o(n,e)}},{100:100,31:31,32:32,44:44,73:73,8:8}],72:[function(t,e,n){var r=t(8),i=t(45),o=t(118),s=Object.defineProperty;n.f=t(30)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return s(t,e,n)}catch(a){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},{118:118,30:30,45:45,8:8}],73:[function(t,e,n){var o=t(72),s=t(8),p=t(81);e.exports=t(30)?Object.defineProperties:function(t,e){s(t);for(var n,a=p(e),r=a.length,i=0;ir;)o(a,n=e[r++])&&(~p(i,n)||i.push(n));return i}},{100:100,115:115,12:12,42:42}],81:[function(t,e,n){var a=t(80),r=t(32);e.exports=Object.keys||function(t){return a(t,r)}},{32:32,80:80}],82:[function(t,e,n){n.f={}.propertyIsEnumerable},{}],83:[function(t,e,n){var r=t(34),i=t(24),o=t(36);e.exports=function(t,e){var n=(i.Object||{})[t]||Object[t],a={};a[t]=e(n),r(r.S+r.F*o(function(){n(1)}),"Object",a)}},{24:24,34:34,36:36}],84:[function(t,e,n){var p=t(81),u=t(115),c=t(82).f;e.exports=function(s){return function(t){for(var e,n=u(t),a=p(n),r=a.length,i=0,o=[];i>>0||(o.test(n)?16:10))}:a},{109:109,110:110,41:41}],88:[function(t,e,n){e.exports=function(t){try{return{e:!1,v:t()}}catch(e){return{e:!0,v:e}}}},{}],89:[function(t,e,n){var a=t(8),r=t(52),i=t(69);e.exports=function(t,e){if(a(t),r(e)&&e.constructor===t)return e;var n=i.f(t);return(0,n.resolve)(e),n.promise}},{52:52,69:69,8:8}],90:[function(t,e,n){e.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},{}],91:[function(t,e,n){var r=t(92);e.exports=function(t,e,n){for(var a in e)r(t,a,e[a],n);return t}},{92:92}],92:[function(t,e,n){var i=t(41),o=t(43),s=t(42),p=t(122)("src"),a="toString",r=Function[a],u=(""+r).split(a);t(24).inspectSource=function(t){return r.call(t)},(e.exports=function(t,e,n,a){var r="function"==typeof n;r&&(s(n,"name")||o(n,"name",e)),t[e]!==n&&(r&&(s(n,p)||o(n,p,t[e]?""+t[e]:u.join(String(e)))),t===i?t[e]=n:a?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n)))})(Function.prototype,a,function(){return"function"==typeof this&&this[p]||r.call(this)})},{122:122,24:24,41:41,42:42,43:43}],93:[function(t,e,n){e.exports=function(e,n){var a=n===Object(n)?function(t){return n[t]}:n;return function(t){return String(t).replace(e,a)}}},{}],94:[function(t,e,n){e.exports=Object.is||function(t,e){return t===e?0!==t||1/t==1/e:t!=t&&e!=e}},{}],95:[function(t,e,n){"use strict";var a=t(34),o=t(4),s=t(26),p=t(40);e.exports=function(t){a(a.S,t,{from:function(t){var e,n,a,r,i=arguments[1];return o(this),(e=i!==undefined)&&o(i),t==undefined?new this:(n=[],e?(a=0,r=s(i,arguments[2],2),p(t,!1,function(t){n.push(r(t,a++))})):p(t,!1,n.push,n),new this(n))}})}},{26:26,34:34,4:4,40:40}],96:[function(t,e,n){"use strict";var a=t(34);e.exports=function(t){a(a.S,t,{of:function(){for(var t=arguments.length,e=new Array(t);t--;)e[t]=arguments[t];return new this(e)}})}},{34:34}],97:[function(r,t,e){function i(t,e){if(a(t),!n(e)&&null!==e)throw TypeError(e+": can't set as prototype!")}var n=r(52),a=r(8);t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,a){try{(a=r(26)(Function.call,r(75).f(Object.prototype,"__proto__").set,2))(t,[]),n=!(t instanceof Array)}catch(e){n=!0}return function(t,e){return i(t,e),n?t.__proto__=e:a(t,e),t}}({},!1):undefined),check:i}},{26:26,52:52,75:75,8:8}],98:[function(t,e,n){"use strict";var a=t(41),r=t(72),i=t(30),o=t(127)("species");e.exports=function(t){var e=a[t];i&&e&&!e[o]&&r.f(e,o,{configurable:!0,get:function(){return this}})}},{127:127,30:30,41:41,72:72}],99:[function(t,e,n){var a=t(72).f,r=t(42),i=t(127)("toStringTag");e.exports=function(t,e,n){t&&!r(t=n?t:t.prototype,i)&&a(t,i,{configurable:!0,value:e})}},{127:127,42:42,72:72}],100:[function(t,e,n){var a=t(101)("keys"),r=t(122);e.exports=function(t){return a[t]||(a[t]=r(t))}},{101:101,122:122}],101:[function(t,e,n){var a=t(24),r=t(41),i="__core-js_shared__",o=r[i]||(r[i]={});(e.exports=function(t,e){return o[t]||(o[t]=e!==undefined?e:{})})("versions",[]).push({version:a.version,mode:t(60)?"pure":"global",copyright:"© 2018 Denis Pushkarev (zloirock.ru)"})},{24:24,41:41,60:60}],102:[function(t,e,n){var r=t(8),i=t(4),o=t(127)("species");e.exports=function(t,e){var n,a=r(t).constructor;return a===undefined||(n=r(a)[o])==undefined?e:i(n)}},{127:127,4:4,8:8}],103:[function(t,e,n){"use strict";var a=t(36);e.exports=function(t,e){return!!t&&a(function(){e?t.call(null,function(){},1):t.call(null)})}},{36:36}],104:[function(t,e,n){var p=t(114),u=t(29);e.exports=function(s){return function(t,e){var n,a,r=String(u(t)),i=p(e),o=r.length;return i<0||o<=i?s?"":undefined:(n=r.charCodeAt(i))<55296||56319"+r+""}var r=t(34),i=t(36),o=t(29),s=/"/g;e.exports=function(e,t){var n={};n[e]=t(a),r(r.P+r.F*i(function(){var t=""[e]('"');return t!==t.toLowerCase()||3p&&(u=u.slice(0,p)),a?u+r:r+u}},{108:108,116:116,29:29}],108:[function(t,e,n){"use strict";var r=t(114),i=t(29);e.exports=function(t){var e=String(i(this)),n="",a=r(t);if(a<0||a==Infinity)throw RangeError("Count can't be negative");for(;0>>=1)&&(e+=e))1&a&&(n+=e);return n}},{114:114,29:29}],109:[function(t,e,n){function a(t,e,n){var a={},r=s(function(){return!!p[t]()||"​…"!="​…"[t]()}),i=a[t]=r?e(l):p[t];n&&(a[n]=i),o(o.P+o.F*r,"String",a)}var o=t(34),r=t(29),s=t(36),p=t(110),i="["+p+"]",u=RegExp("^"+i+i+"*"),c=RegExp(i+i+"*$"),l=a.trim=function(t,e){return t=String(r(t)),1&e&&(t=t.replace(u,"")),2&e&&(t=t.replace(c,"")),t};e.exports=a},{110:110,29:29,34:34,36:36}],110:[function(t,e,n){e.exports="\t\n\x0B\f\r   ᠎              \u2028\u2029\ufeff"},{}],111:[function(t,e,n){function a(){var t=+this;if(y.hasOwnProperty(t)){var e=y[t];delete y[t],e()}}function r(t){a.call(t.data)}var i,o,s,p=t(26),u=t(47),c=t(44),l=t(31),d=t(41),f=d.process,h=d.setImmediate,m=d.clearImmediate,g=d.MessageChannel,b=d.Dispatch,v=0,y={},_="onreadystatechange";h&&m||(h=function(t){for(var e=[],n=1;n>1,c=23===e?O(2,-24)-O(2,-77):0,l=0,d=t<0||0===t&&1/t<0?1:0;for((t=A(t))!=t||t===C?(r=t!=t?1:0,a=p):(a=T(R(t)/M),t*(i=O(2,-a))<1&&(a--,i*=2),2<=(t+=1<=a+u?c/i:c*O(2,1-u))*i&&(a++,i/=2),p<=a+u?(r=0,a=p):1<=a+u?(r=(t*i-1)*O(2,e),a+=u):(r=t*O(2,u-1)*O(2,e),a=0));8<=e;o[l++]=255&r,r/=256,e-=8);for(a=a<>1,s=r-7,p=n-1,u=t[p--],c=127&u;for(u>>=7;0>=-s,s+=e;0>8&255]}function G(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]}function z(t){return I(t,52,8)}function W(t){return I(t,23,4)}function H(t,e,n){m(t[_],e,{get:function(){return this[n]}})}function K(t,e,n,a){var r=f(+n);if(r+e>t[N])throw E(x);var i=t[D]._b,o=r+t[F],s=i.slice(o,o+e);return a?s:s.reverse()}function Q(t,e,n,a,r,i){var o=f(+n);if(o+e>t[N])throw E(x);for(var s=t[D]._b,p=o+t[F],u=a(+r),c=0;cJ;)(Y=X[J++])in w||s(w,Y,P[Y]);i||($.constructor=w)}var Z=new k(new w(2)),tt=k[_].setInt8;Z.setInt8(0,2147483648),Z.setInt8(1,2147483649),!Z.getInt8(0)&&Z.getInt8(1)||p(k[_],{setInt8:function(t,e){tt.call(this,t,e<<24>>24)},setUint8:function(t,e){tt.call(this,t,e<<24>>24)}},!0)}else w=function(t){c(this,w,v);var e=f(t);this._b=g.call(new Array(e),0),this[N]=e},k=function(t,e,n){c(this,k,y),c(t,w,y);var a=t[N],r=l(e);if(r<0||a>24},getUint8:function(t){return K(this,1,t)[0]},getInt16:function(t){var e=K(this,2,t,arguments[1]);return(e[1]<<8|e[0])<<16>>16},getUint16:function(t){var e=K(this,2,t,arguments[1]);return e[1]<<8|e[0]},getInt32:function(t){return V(K(this,4,t,arguments[1]))},getUint32:function(t){return V(K(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return B(K(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return B(K(this,8,t,arguments[1]),52,8)},setInt8:function(t,e){Q(this,1,t,U,e)},setUint8:function(t,e){Q(this,1,t,U,e)},setInt16:function(t,e){Q(this,2,t,q,e,arguments[2])},setUint16:function(t,e){Q(this,2,t,q,e,arguments[2])},setInt32:function(t,e){Q(this,4,t,G,e,arguments[2])},setUint32:function(t,e){Q(this,4,t,G,e,arguments[2])},setFloat32:function(t,e){Q(this,4,t,W,e,arguments[2])},setFloat64:function(t,e){Q(this,8,t,z,e,arguments[2])}});b(w,v),b(k,y),s(k[_],o.VIEW,!0),n[v]=w,n[y]=k},{10:10,113:113,114:114,116:116,121:121,30:30,36:36,41:41,43:43,60:60,7:7,72:72,77:77,91:91,99:99}],121:[function(t,e,n){for(var a,r=t(41),i=t(43),o=t(122),s=o("typed_array"),p=o("view"),u=!(!r.ArrayBuffer||!r.DataView),c=u,l=0,d="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");l<9;)(a=r[d[l++]])?(i(a.prototype,s,!0),i(a.prototype,p,!0)):c=!1;e.exports={ABV:u,CONSTR:c,TYPED:s,VIEW:p}},{122:122,41:41,43:43}],122:[function(t,e,n){var a=0,r=Math.random();e.exports=function(t){return"Symbol(".concat(t===undefined?"":t,")_",(++a+r).toString(36))}},{}],123:[function(t,e,n){var a=t(41).navigator;e.exports=a&&a.userAgent||""},{41:41}],124:[function(t,e,n){var a=t(52);e.exports=function(t,e){if(!a(t)||t._t!==e)throw TypeError("Incompatible receiver, "+e+" required!");return t}},{52:52}],125:[function(t,e,n){var a=t(41),r=t(24),i=t(60),o=t(126),s=t(72).f;e.exports=function(t){var e=r.Symbol||(r.Symbol=i?{}:a.Symbol||{});"_"==t.charAt(0)||t in e||s(e,t,{value:o.f(t)})}},{126:126,24:24,41:41,60:60,72:72}],126:[function(t,e,n){n.f=t(127)},{127:127}],127:[function(t,e,n){var a=t(101)("wks"),r=t(122),i=t(41).Symbol,o="function"==typeof i;(e.exports=function(t){return a[t]||(a[t]=o&&i[t]||(o?i:r)("Symbol."+t))}).store=a},{101:101,122:122,41:41}],128:[function(t,e,n){var a=t(18),r=t(127)("iterator"),i=t(59);e.exports=t(24).getIteratorMethod=function(t){if(t!=undefined)return t[r]||t["@@iterator"]||i[a(t)]}},{127:127,18:18,24:24,59:59}],129:[function(t,e,n){var a=t(34),r=t(93)(/[\\^$*+?.()|[\]{}]/g,"\\$&");a(a.S,"RegExp",{escape:function(t){return r(t)}})},{34:34,93:93}],130:[function(t,e,n){var a=t(34);a(a.P,"Array",{copyWithin:t(9)}),t(6)("copyWithin")},{34:34,6:6,9:9}],131:[function(t,e,n){"use strict";var a=t(34),r=t(13)(4);a(a.P+a.F*!t(103)([].every,!0),"Array",{every:function(t){return r(this,t,arguments[1])}})},{103:103,13:13,34:34}],132:[function(t,e,n){var a=t(34);a(a.P,"Array",{fill:t(10)}),t(6)("fill")},{10:10,34:34,6:6}],133:[function(t,e,n){"use strict";var a=t(34),r=t(13)(2);a(a.P+a.F*!t(103)([].filter,!0),"Array",{filter:function(t){return r(this,t,arguments[1])}})},{103:103,13:13,34:34}],134:[function(t,e,n){"use strict";var a=t(34),r=t(13)(6),i="findIndex",o=!0;i in[]&&Array(1)[i](function(){o=!1}),a(a.P+a.F*o,"Array",{findIndex:function(t){return r(this,t,1=t.length?(this._t=undefined,r(1)):r(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])},"values"),i.Arguments=i.Array,a("keys"),a("values"),a("entries")},{115:115,56:56,58:58,59:59,6:6}],141:[function(t,e,n){"use strict";var a=t(34),r=t(115),i=[].join;a(a.P+a.F*(t(48)!=Object||!t(103)(i)),"Array",{join:function(t){return i.call(r(this),t===undefined?",":t)}})},{103:103,115:115,34:34,48:48}],142:[function(t,e,n){"use strict";var a=t(34),r=t(115),i=t(114),o=t(116),s=[].lastIndexOf,p=!!s&&1/[1].lastIndexOf(1,-0)<0;a(a.P+a.F*(p||!t(103)(s)),"Array",{lastIndexOf:function(t){if(p)return s.apply(this,arguments)||0;var e=r(this),n=o(e.length),a=n-1;for(1>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},{34:34}],165:[function(t,e,n){var a=t(34),r=Math.exp;a(a.S,"Math",{cosh:function(t){return(r(t=+t)+r(-t))/2}})},{34:34}],166:[function(t,e,n){var a=t(34),r=t(61);a(a.S+a.F*(r!=Math.expm1),"Math",{expm1:r})},{34:34,61:61}],167:[function(t,e,n){var a=t(34);a(a.S,"Math",{fround:t(62)})},{34:34,62:62}],168:[function(t,e,n){var a=t(34),p=Math.abs;a(a.S,"Math",{hypot:function(t,e){for(var n,a,r=0,i=0,o=arguments.length,s=0;i>>16)*o+i*(n&r>>>16)<<16>>>0)}})},{34:34,36:36}],170:[function(t,e,n){var a=t(34);a(a.S,"Math",{log10:function(t){return Math.log(t)*Math.LOG10E}})},{34:34}],171:[function(t,e,n){var a=t(34);a(a.S,"Math",{log1p:t(63)})},{34:34,63:63}],172:[function(t,e,n){var a=t(34);a(a.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},{34:34}],173:[function(t,e,n){var a=t(34);a(a.S,"Math",{sign:t(65)})},{34:34,65:65}],174:[function(t,e,n){var a=t(34),r=t(61),i=Math.exp;a(a.S+a.F*t(36)(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(r(t)-r(-t))/2:(i(t-1)-i(-t-1))*(Math.E/2)}})},{34:34,36:36,61:61}],175:[function(t,e,n){var a=t(34),r=t(61),i=Math.exp;a(a.S,"Math",{tanh:function(t){var e=r(t=+t),n=r(-t);return e==Infinity?1:n==Infinity?-1:(e-n)/(i(t)+i(-t))}})},{34:34,61:61}],176:[function(t,e,n){var a=t(34);a(a.S,"Math",{trunc:function(t){return(0w;w++)i(g,_=x[w])&&!i(m,_)&&d(m,_,l(g,_));(m.prototype=b).constructor=m,t(92)(r,h,m)}},{109:109,118:118,19:19,30:30,36:36,41:41,42:42,46:46,71:71,72:72,75:75,77:77,92:92}],178:[function(t,e,n){var a=t(34);a(a.S,"Number",{EPSILON:Math.pow(2,-52)})},{34:34}],179:[function(t,e,n){var a=t(34),r=t(41).isFinite;a(a.S,"Number",{isFinite:function(t){return"number"==typeof t&&r(t)}})},{34:34,41:41}],180:[function(t,e,n){var a=t(34);a(a.S,"Number",{isInteger:t(51)})},{34:34,51:51}],181:[function(t,e,n){var a=t(34);a(a.S,"Number",{isNaN:function(t){return t!=t}})},{34:34}],182:[function(t,e,n){var a=t(34),r=t(51),i=Math.abs;a(a.S,"Number",{isSafeInteger:function(t){return r(t)&&i(t)<=9007199254740991}})},{34:34,51:51}],183:[function(t,e,n){var a=t(34);a(a.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},{34:34}],184:[function(t,e,n){var a=t(34);a(a.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},{34:34}],185:[function(t,e,n){var a=t(34),r=t(86);a(a.S+a.F*(Number.parseFloat!=r),"Number",{parseFloat:r})},{34:34,86:86}],186:[function(t,e,n){var a=t(34),r=t(87);a(a.S+a.F*(Number.parseInt!=r),"Number",{parseInt:r})},{34:34,87:87}],187:[function(t,e,n){"use strict";function u(t,e){for(var n=-1,a=e;++n<6;)a+=t*o[n],o[n]=a%1e7,a=i(a/1e7)}function c(t){for(var e=6,n=0;0<=--e;)n+=o[e],o[e]=i(n/t),n=n%t*1e7}function l(){for(var t=6,e="";0<=--t;)if(""!==e||0===t||0!==o[t]){var n=String(o[t]);e=""===e?n:e+h.call("0",7-n.length)+n}return e}var a=t(34),d=t(114),f=t(5),h=t(108),r=1..toFixed,i=Math.floor,o=[0,0,0,0,0,0],m="Number.toFixed: incorrect invocation!",g=function(t,e,n){return 0===e?n:e%2==1?g(t,e-1,n*t):g(t*t,e/2,n)};a(a.P+a.F*(!!r&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!t(36)(function(){r.call({})})),"Number",{toFixed:function(t){var e,n,a,r,i=f(this,m),o=d(t),s="",p="0";if(o<0||20t;)e(a[t++]);l._c=[],l._n=!1,n&&!l._h&&N(l)})}}function o(t){var e=this;e._d||(e._d=!0,(e=e._w||e)._v=t,e._s=2,e._a||(e._a=e._c.slice()),i(e,!0))}var n,s,p,u,c=a(60),l=a(41),f=a(26),h=a(18),m=a(34),g=a(52),b=a(4),v=a(7),y=a(40),_=a(102),x=a(111).set,w=a(68)(),k=a(69),S=a(88),E=a(123),C=a(89),P="Promise",A=l.TypeError,O=l.process,T=O&&O.versions,R=T&&T.v8||"",M=l[P],L="process"==h(O),j=s=k.f,D=!!function(){try{var t=M.resolve(1),e=(t.constructor={})[a(127)("species")]=function(t){t(r,r)};return(L||"function"==typeof PromiseRejectionEvent)&&t.then(r)instanceof e&&0!==R.indexOf("6.6")&&-1===E.indexOf("Chrome/66")}catch(n){}}(),N=function(i){x.call(l,function(){var t,e,n,a=i._v,r=F(i);if(r&&(t=S(function(){L?O.emit("unhandledRejection",a,i):(e=l.onunhandledrejection)?e({promise:i,reason:a}):(n=l.console)&&n.error&&n.error("Unhandled promise rejection",a)}),i._h=L||F(i)?2:1),i._a=undefined,r&&t.e)throw t.v})},F=function(t){return 1!==t._h&&0===(t._a||t._c).length},I=function(e){x.call(l,function(){var t;L?O.emit("rejectionHandled",e):(t=l.onrejectionhandled)&&t({promise:e,reason:e._v})})},B=function(n){var a,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===n)throw A("Promise can't be resolved itself");(a=d(n))?w(function(){var t={_w:r,_d:!1};try{a.call(n,f(B,t,1),f(o,t,1))}catch(e){o.call(t,e)}}):(r._v=n,r._s=1,i(r,!1))}catch(t){o.call({_w:r,_d:!1},t)}}};D||(M=function(t){v(this,M,P,"_h"),b(t),n.call(this);try{t(f(B,this,1),f(o,this,1))}catch(e){o.call(this,e)}},(n=function(t){this._c=[],this._a=undefined,this._s=0,this._d=!1,this._v=undefined,this._h=0,this._n=!1}).prototype=a(91)(M.prototype,{then:function(t,e){var n=j(_(this,M));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=L?O.domain:undefined,this._c.push(n),this._a&&this._a.push(n),this._s&&i(this,!1),n.promise},"catch":function(t){return this.then(undefined,t)}}),p=function(){var t=new n;this.promise=t,this.resolve=f(B,t,1),this.reject=f(o,t,1)},k.f=j=function(t){return t===M||t===u?new p(t):s(t)}),m(m.G+m.W+m.F*!D,{Promise:M}),a(99)(M,P),a(98)(P),u=a(24)[P],m(m.S+m.F*!D,P,{reject:function(t){var e=j(this);return(0,e.reject)(t),e.promise}}),m(m.S+m.F*(c||!D),P,{resolve:function(t){return C(c&&this===u?M:this,t)}}),m(m.S+m.F*!(D&&a(57)(function(t){M.all(t)["catch"](r)})),P,{all:function(t){var o=this,e=j(o),s=e.resolve,p=e.reject,n=S(function(){var a=[],r=0,i=1;y(t,!1,function(t){var e=r++,n=!1;a.push(undefined),i++,o.resolve(t).then(function(t){n||(n=!0,a[e]=t,--i||s(a))},p)}),--i||s(a)});return n.e&&p(n.v),e.promise},race:function(t){var e=this,n=j(e),a=n.reject,r=S(function(){y(t,!1,function(t){e.resolve(t).then(n.resolve,a)})});return r.e&&a(r.v),n.promise}})},{102:102,111:111,123:123,127:127,18:18,24:24,26:26,34:34,4:4,40:40,41:41,52:52,57:57,60:60,68:68,69:69,7:7,88:88,89:89,91:91,98:98,99:99}],209:[function(t,e,n){var a=t(34),i=t(4),o=t(8),s=(t(41).Reflect||{}).apply,p=Function.apply;a(a.S+a.F*!t(36)(function(){s(function(){})}),"Reflect",{apply:function(t,e,n){var a=i(t),r=o(n);return s?s(a,e,r):p.call(a,e,r)}})},{34:34,36:36,4:4,41:41,8:8}],210:[function(t,e,n){var a=t(34),s=t(71),p=t(4),u=t(8),c=t(52),r=t(36),l=t(17),d=(t(41).Reflect||{}).construct,f=r(function(){function t(){}return!(d(function(){},[],t)instanceof t)}),h=!r(function(){d(function(){})});a(a.S+a.F*(f||h),"Reflect",{construct:function(t,e){p(t),u(e);var n=arguments.length<3?t:p(arguments[2]);if(h&&!f)return d(t,e,n);if(t==n){switch(e.length){case 0:return new t;case 1:return new t(e[0]);case 2:return new t(e[0],e[1]);case 3:return new t(e[0],e[1],e[2]);case 4:return new t(e[0],e[1],e[2],e[3])}var a=[null];return a.push.apply(a,e),new(l.apply(t,a))}var r=n.prototype,i=s(c(r)?r:Object.prototype),o=Function.apply.call(t,i,e);return c(o)?o:i}})},{17:17,34:34,36:36,4:4,41:41,52:52,71:71,8:8}],211:[function(t,e,n){var r=t(72),a=t(34),i=t(8),o=t(118);a(a.S+a.F*t(36)(function(){Reflect.defineProperty(r.f({},1,{value:1}),1,{value:2})}),"Reflect",{defineProperty:function(t,e,n){i(t),e=o(e,!0),i(n);try{return r.f(t,e,n),!0}catch(a){return!1}}})},{118:118,34:34,36:36,72:72,8:8}],212:[function(t,e,n){var a=t(34),r=t(75).f,i=t(8);a(a.S,"Reflect",{deleteProperty:function(t,e){var n=r(i(t),e);return!(n&&!n.configurable)&&delete t[e]}})},{34:34,75:75,8:8}],213:[function(t,e,n){"use strict";function a(t){this._t=i(t),this._i=0;var e,n=this._k=[];for(e in t)n.push(e)}var r=t(34),i=t(8);t(55)(a,"Object",function(){var t,e=this._k;do{if(this._i>=e.length)return{value:undefined,done:!0}}while(!((t=e[this._i++])in this._t));return{value:t,done:!1}}),r(r.S,"Reflect",{enumerate:function(t){return new a(t)}})},{34:34,55:55,8:8}],214:[function(t,e,n){var a=t(75),r=t(34),i=t(8);r(r.S,"Reflect",{getOwnPropertyDescriptor:function(t,e){return a.f(i(t),e)}})},{34:34,75:75,8:8}],215:[function(t,e,n){var a=t(34),r=t(79),i=t(8);a(a.S,"Reflect",{getPrototypeOf:function(t){return r(i(t))}})},{34:34,79:79,8:8}],216:[function(t,e,n){var i=t(75),o=t(79),s=t(42),a=t(34),p=t(52),u=t(8);a(a.S,"Reflect",{get:function c(t,e){var n,a,r=arguments.length<3?t:arguments[2];return u(t)===r?t[e]:(n=i.f(t,e))?s(n,"value")?n.value:n.get!==undefined?n.get.call(r):undefined:p(a=o(t))?c(a,e,r):void 0}})},{34:34,42:42,52:52,75:75,79:79,8:8}],217:[function(t,e,n){var a=t(34);a(a.S,"Reflect",{has:function(t,e){return e in t}})},{34:34}],218:[function(t,e,n){var a=t(34),r=t(8),i=Object.isExtensible;a(a.S,"Reflect",{isExtensible:function(t){return r(t),!i||i(t)}})},{34:34,8:8}],219:[function(t,e,n){var a=t(34);a(a.S,"Reflect",{ownKeys:t(85)})},{34:34,85:85}],220:[function(t,e,n){var a=t(34),r=t(8),i=Object.preventExtensions;a(a.S,"Reflect",{preventExtensions:function(t){r(t);try{return i&&i(t),!0}catch(e){return!1}}})},{34:34,8:8}],221:[function(t,e,n){var a=t(34),r=t(97);r&&a(a.S,"Reflect",{setPrototypeOf:function(t,e){r.check(t,e);try{return r.set(t,e),!0}catch(n){return!1}}})},{34:34,97:97}],222:[function(t,e,n){var s=t(72),p=t(75),u=t(79),c=t(42),a=t(34),l=t(90),d=t(8),f=t(52);a(a.S,"Reflect",{set:function h(t,e,n){var a,r,i=arguments.length<4?t:arguments[3],o=p.f(d(t),e);if(!o){if(f(r=u(t)))return h(r,e,n,i);o=l(0)}if(c(o,"value")){if(!1===o.writable||!f(i))return!1;if(a=p.f(i,e)){if(a.get||a.set||!1===a.writable)return!1;a.value=n,s.f(i,e,a)}else s.f(i,e,l(0,n));return!0}return o.set!==undefined&&(o.set.call(i,n),!0)}})},{34:34,42:42,52:52,72:72,75:75,79:79,8:8,90:90}],223:[function(t,e,n){var a=t(41),i=t(46),r=t(72).f,o=t(77).f,s=t(53),p=t(38),u=a.RegExp,c=u,l=u.prototype,d=/a/g,f=/a/g,h=new u(d)!==d;if(t(30)&&(!h||t(36)(function(){return f[t(127)("match")]=!1,u(d)!=d||u(f)==f||"/a/i"!=u(d,"i")}))){u=function(t,e){var n=this instanceof u,a=s(t),r=e===undefined;return!n&&a&&t.constructor===u&&r?t:i(h?new c(a&&!r?t.source:t,e):c((a=t instanceof u)?t.source:t,a&&r?p.call(t):e),n?this:l,u)};function m(e){e in u||r(u,e,{configurable:!0,get:function(){return c[e]},set:function(t){c[e]=t}})}for(var g=o(c),b=0;g.length>b;)m(g[b++]);(l.constructor=u).prototype=l,t(92)(a,"RegExp",u)}t(98)("RegExp")},{127:127,30:30,36:36,38:38,41:41,46:46,53:53,72:72,77:77,92:92,98:98}],224:[function(t,e,n){t(30)&&"g"!=/./g.flags&&t(72).f(RegExp.prototype,"flags",{configurable:!0,get:t(38)})},{30:30,38:38,72:72}],225:[function(t,e,n){t(37)("match",1,function(a,r,t){return[function(t){"use strict";var e=a(this),n=t==undefined?undefined:t[r];return n!==undefined?n.call(t,e):new RegExp(t)[r](String(e))},t]})},{37:37}],226:[function(t,e,n){t(37)("replace",2,function(r,i,o){return[function(t,e){"use strict";var n=r(this),a=t==undefined?undefined:t[i];return a!==undefined?a.call(t,n,e):o.call(String(n),t,e)},o]})},{37:37}],227:[function(t,e,n){t(37)("search",1,function(a,r,t){return[function(t){"use strict";var e=a(this),n=t==undefined?undefined:t[r];return n!==undefined?n.call(t,e):new RegExp(t)[r](String(e))},t]})},{37:37}],228:[function(e,t,n){e(37)("split",2,function(r,i,o){"use strict";var f=e(53),h=o,m=[].push,t="split",g="length",b="lastIndex";if("c"=="abbc"[t](/(b)*/)[1]||4!="test"[t](/(?:)/,-1)[g]||2!="ab"[t](/(?:ab)*/)[g]||4!="."[t](/(.?)(.?)/)[g]||1<"."[t](/()()/)[g]||""[t](/.?/)[g]){var v=/()??/.exec("")[1]===undefined;o=function(t,e){var n=String(this);if(t===undefined&&0===e)return[];if(!f(t))return h.call(n,t,e);var a,r,i,o,s,p=[],u=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),c=0,l=e===undefined?4294967295:e>>>0,d=new RegExp(t.source,u+"g");for(v||(a=new RegExp("^"+d.source+"$(?!\\s)",u));(r=d.exec(n))&&!(c<(i=r.index+r[0][g])&&(p.push(n.slice(c,r.index)),!v&&1=l));)d[b]===r.index&&d[b]++;return c===n[g]?!o&&d.test("")||p.push(""):p.push(n.slice(c)),p[g]>l?p.slice(0,l):p}}else"0"[t](undefined,0)[g]&&(o=function(t,e){return t===undefined&&0===e?[]:h.call(this,t,e)});return[function(t,e){var n=r(this),a=t==undefined?undefined:t[i];return a!==undefined?a.call(t,n,e):o.call(String(n),t,e)},o]})},{37:37,53:53}],229:[function(e,t,n){"use strict";e(224);function a(t){e(92)(RegExp.prototype,s,t,!0)}var r=e(8),i=e(38),o=e(30),s="toString",p=/./[s];e(36)(function(){return"/a/b"!=p.call({source:"a",flags:"b"})})?a(function(){var t=r(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):undefined)}):p.name!=s&&a(function(){return p.call(this)})},{224:224,30:30,36:36,38:38,8:8,92:92}],230:[function(t,e,n){"use strict";var a=t(20),r=t(124);e.exports=t(23)("Set",function(t){return function(){return t(this,0>10),e%1024+56320))}return n.join("")}})},{112:112,34:34}],241:[function(t,e,n){"use strict";var a=t(34),r=t(105),i="includes";a(a.P+a.F*t(35)(i),"String",{includes:function(t){return!!~r(this,t,i).indexOf(t,1=e.length?{value:undefined,done:!0}:(t=a(e,n),this._i+=t.length,{value:t,done:!1})})},{104:104,56:56}],244:[function(t,e,n){"use strict";t(106)("link",function(e){return function(t){return e(this,"a","href",t)}})},{106:106}],245:[function(t,e,n){var a=t(34),o=t(115),s=t(116);a(a.S,"String",{raw:function(t){for(var e=o(t.raw),n=s(e.length),a=arguments.length,r=[],i=0;ir;)c(W,e=n[r++])||e==U||e==h||a.push(e);return a}function p(t){for(var e,n=t===K,a=N(n?H:C(t)),r=[],i=0;a.length>i;)!c(W,e=a[i++])||n&&!c(K,e)||r.push(W[e]);return r}var u=t(41),c=t(42),l=t(30),d=t(34),f=t(92),h=t(66).KEY,m=t(36),g=t(101),b=t(99),v=t(122),y=t(127),_=t(126),x=t(125),w=t(33),k=t(50),S=t(8),E=t(52),C=t(115),P=t(118),A=t(90),O=t(71),T=t(76),R=t(75),M=t(72),L=t(81),j=R.f,D=M.f,N=T.f,F=u.Symbol,I=u.JSON,B=I&&I.stringify,V="prototype",U=y("_hidden"),q=y("toPrimitive"),G={}.propertyIsEnumerable,z=g("symbol-registry"),W=g("symbols"),H=g("op-symbols"),K=Object[V],Q="function"==typeof F,Y=u.QObject,$=!Y||!Y[V]||!Y[V].findChild,X=l&&m(function(){return 7!=O(D({},"a",{get:function(){return D(this,"a",{value:7}).a}})).a})?function(t,e,n){var a=j(K,e);a&&delete K[e],D(t,e,n),a&&t!==K&&D(K,e,a)}:D,J=Q&&"symbol"==typeof F.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof F},Z=function(t,e,n){return t===K&&Z(H,e,n),S(t),e=P(e,!0),S(n),c(W,e)?(n.enumerable?(c(t,U)&&t[U][e]&&(t[U][e]=!1),n=O(n,{enumerable:A(0,!1)})):(c(t,U)||D(t,U,A(1,{})),t[U][e]=!0),X(t,e,n)):D(t,e,n)};Q||(f((F=function(){if(this instanceof F)throw TypeError("Symbol is not a constructor!");var e=v(0et;)y(tt[et++]);for(var nt=L(y.store),at=0;nt.length>at;)x(nt[at++]);d(d.S+d.F*!Q,"Symbol",{"for":function(t){return c(z,t+="")?z[t]:z[t]=F(t)},keyFor:function(t){if(!J(t))throw TypeError(t+" is not a symbol!");for(var e in z)if(z[e]===t)return e},useSetter:function(){$=!0},useSimple:function(){$=!1}}),d(d.S+d.F*!Q,"Object",{create:function(t,e){return e===undefined?O(t):r(O(t),e)},defineProperty:Z,defineProperties:r,getOwnPropertyDescriptor:o,getOwnPropertyNames:s,getOwnPropertySymbols:p}),I&&d(d.S+d.F*(!Q||m(function(){var t=F();return"[null]"!=B([t])||"{}"!=B({a:t})||"{}"!=B(Object(t))})),"JSON",{stringify:function(t){for(var e,n,a=[t],r=1;r>>0,i=n>>>0;return(e>>>0)+(a>>>0)+((r&i|(r|i)&~(r+i>>>0))>>>31)|0}})},{34:34}],281:[function(t,e,n){var a=t(34);a(a.S,"Math",{imulh:function(t,e){var n=+t,a=+e,r=65535&n,i=65535&a,o=n>>16,s=a>>16,p=(o*i>>>0)+(r*i>>>16);return o*s+(p>>16)+((r*s>>>0)+(65535&p)>>16)}})},{34:34}],282:[function(t,e,n){var a=t(34);a(a.S,"Math",{isubh:function(t,e,n,a){var r=t>>>0,i=n>>>0;return(e>>>0)-(a>>>0)-((~r&i|~(r^i)&r-i>>>0)>>>31)|0}})},{34:34}],283:[function(t,e,n){var a=t(34);a(a.S,"Math",{RAD_PER_DEG:180/Math.PI})},{34:34}],284:[function(t,e,n){var a=t(34),r=Math.PI/180;a(a.S,"Math",{radians:function(t){return t*r}})},{34:34}],285:[function(t,e,n){var a=t(34);a(a.S,"Math",{scale:t(64)})},{34:34,64:64}],286:[function(t,e,n){var a=t(34);a(a.S,"Math",{signbit:function(t){return(t=+t)!=t?t:0==t?1/t==Infinity:0>>16,s=a>>>16,p=(o*i>>>0)+(r*i>>>16);return o*s+(p>>>16)+((r*s>>>0)+(65535&p)>>>16)}})},{34:34}],288:[function(t,e,n){"use strict";var a=t(34),r=t(117),i=t(4),o=t(72);t(30)&&a(a.P+t(74),"Object",{__defineGetter__:function(t,e){o.f(r(this),t,{get:i(e),enumerable:!0,configurable:!0})}})},{117:117,30:30,34:34,4:4,72:72,74:74}],289:[function(t,e,n){"use strict";var a=t(34),r=t(117),i=t(4),o=t(72);t(30)&&a(a.P+t(74),"Object",{__defineSetter__:function(t,e){o.f(r(this),t,{set:i(e),enumerable:!0,configurable:!0})}})},{117:117,30:30,34:34,4:4,72:72,74:74}],290:[function(t,e,n){var a=t(34),r=t(84)(!0);a(a.S,"Object",{entries:function(t){return r(t)}})},{34:34,84:84}],291:[function(t,e,n){var a=t(34),p=t(85),u=t(115),c=t(75),l=t(25);a(a.S,"Object",{getOwnPropertyDescriptors:function(t){for(var e,n,a=u(t),r=c.f,i=p(a),o={},s=0;i.length>s;)(n=r(a,e=i[s++]))!==undefined&&l(o,e,n);return o}})},{115:115,25:25,34:34,75:75,85:85}],292:[function(t,e,n){"use strict";var a=t(34),r=t(117),i=t(118),o=t(79),s=t(75).f;t(30)&&a(a.P+t(74),"Object",{__lookupGetter__:function(t){var e,n=r(this),a=i(t,!0);do{if(e=s(n,a))return e.get}while(n=o(n))}})},{117:117,118:118,30:30,34:34,74:74,75:75,79:79}],293:[function(t,e,n){"use strict";var a=t(34),r=t(117),i=t(118),o=t(79),s=t(75).f;t(30)&&a(a.P+t(74),"Object",{__lookupSetter__:function(t){var e,n=r(this),a=i(t,!0);do{if(e=s(n,a))return e.set}while(n=o(n))}})},{117:117,118:118,30:30,34:34,74:74,75:75,79:79}],294:[function(t,e,n){var a=t(34),r=t(84)(!1);a(a.S,"Object",{values:function(t){return r(t)}})},{34:34,84:84}],295:[function(t,e,n){"use strict";function i(t){return null==t?undefined:f(t)}function o(t){var e=t._c;e&&(t._c=undefined,e())}function s(t){return t._o===undefined}function p(t){s(t)||(t._o=undefined,o(t))}function a(t,e){h(t),this._c=undefined,this._o=t,t=new _(this);try{var n=e(t),a=n;null!=n&&("function"==typeof n.unsubscribe?n=function(){a.unsubscribe()}:f(n),this._c=n)}catch(r){return void t.error(r)}s(this)&&o(this)}var r=t(34),u=t(41),c=t(24),l=t(68)(),d=t(127)("observable"),f=t(4),h=t(8),m=t(7),g=t(91),b=t(43),v=t(40),y=v.RETURN;a.prototype=g({},{unsubscribe:function(){p(this)}});var _=function(t){this._s=t};_.prototype=g({},{next:function(t){var e=this._s;if(!s(e)){var n=e._o;try{var a=i(n.next);if(a)return a.call(n,t)}catch(r){try{p(e)}finally{throw r}}}},error:function(t){var e=this._s;if(s(e))throw t;var n=e._o;e._o=undefined;try{var a=i(n.error);if(!a)throw t;t=a.call(n,t)}catch(r){try{o(e)}finally{throw r}}return o(e),t},complete:function(t){var e=this._s;if(!s(e)){var n=e._o;e._o=undefined;try{var a=i(n.complete);t=a?a.call(n,t):undefined}catch(r){try{o(e)}finally{throw r}}return o(e),t}}});var x=function(t){m(this,x,"Observable","_f")._f=f(t)};g(x.prototype,{subscribe:function(t){return new a(t,this._f)},forEach:function(r){var e=this;return new(c.Promise||u.Promise)(function(t,n){f(r);var a=e.subscribe({next:function(t){try{return r(t)}catch(e){n(e),a.unsubscribe()}},error:n,complete:t})})}}),g(x,{from:function(a){var t="function"==typeof this?this:x,e=i(h(a)[d]);if(e){var n=h(e.call(a));return n.constructor===t?n:new t(function(t){return n.subscribe(t)})}return new t(function(e){var n=!1;return l(function(){if(!n){try{if(v(a,!1,function(t){if(e.next(t),n)return y})===y)return}catch(t){if(n)throw t;return void e.error(t)}e.complete()}}),function(){n=!0}})},of:function(){for(var t=0,e=arguments.length,a=new Array(e);t",a.insertBefore(n.lastChild,a.firstChild)}(t,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),s||function a(e,n){n.cache||(n.cache={},n.createElem=e.createElement,n.createFrag=e.createDocumentFragment,n.frag=n.createFrag()),e.createElement=function(t){return h.shivMethods?d(t,e,n):n.createElem(t)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+c().join().replace(/[\w\-:]+/g,function(t){return n.createElem(t),n.frag.createElement(t),'c("'+t+'")'})+");return n}")(h,n.frag)}(t,e),t}!function(){try{var t=o.createElement("a");t.innerHTML="",n="hidden"in t,s=1==t.childNodes.length||function(){o.createElement("a");var t=o.createDocumentFragment();return"undefined"==typeof t.cloneNode||"undefined"==typeof t.createDocumentFragment||"undefined"==typeof t.createElement}()}catch(e){s=n=!0}}();var h={elements:e.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:"3.7.3-pre",shivCSS:!1!==e.shivCSS,supportsUnknownElements:s,shivMethods:!1!==e.shivMethods,type:"default",shivDocument:f,createElement:d,createDocumentFragment:function m(t,e){if(t=t||o,s)return t.createDocumentFragment();for(var n=(e=e||l(t)).frag.cloneNode(),a=0,r=c(),i=r.length;a>>0,"function"!=typeof t)throw new TypeError;for(r=0;r "+t);var i=undefined;(i=n.node||a.fragment&&a.fragment.rendered&&a.find("*"))&&e.push(i)}}console.warn.apply(console,["%cRactive.js: %c"+t,"color: rgb(114, 157, 52);","color: rgb(85, 85, 85);"].concat(e))},nt=function(){console.log.apply(console,arguments)}):at=nt=rt=B;var ft='A function was specified for "%s" %s, but no %s was returned',ht=function(t,e){return'Missing "'+t+'" '+e+" plugin. You may need to download a plugin via http://docs.ractivejs.org/latest/plugins#"+e+"s"};function mt(t,e,n){var a=gt(t,e,n);return a?a[t][n]:null}function gt(t,e,n){for(;e;){if(n in e[t])return e;if(e.isolated)return null;e=e.parent}}var bt=function(t,e,n,a){if(t===e)return vt(e);if(a){var r=mt("interpolators",n,a);if(r)return r(t,e)||vt(e);pt(ht(a,"interpolator"))}return yt.number(t,e)||yt.array(t,e)||yt.object(t,e)||vt(e)};function vt(t){return function(){return t}}var yt={number:function(e,t){var n;return tt(e)&&tt(t)?(n=(t=+t)-(e=+e))?function(t){return e+t*n}:function(){return e}:null},array:function(t,e){var n,a,r,i;if(!X(t)||!X(e))return null;for(n=[],a=[],i=r=Math.min(t.length,e.length);i--;)a[i]=bt(t[i],e[i]);for(i=r;i=this.duration?(null!==r&&(ge.start(this.root),this.root.viewmodel.set(r,this.to),ge.end()),this.step&&this.step(1,this.to),this.complete(this.to),-1===(a=this.root._animations.indexOf(this))&<("Animation was not found"),this.root._animations.splice(a,1),this.running=!1):(e=this.easing?this.easing(t/this.duration):t/this.duration,null!==r&&(n=this.interpolator(e),ge.start(this.root),this.root.viewmodel.set(r,n),ge.end()),this.step&&this.step(e,n),!0))},stop:function(){var t;this.running=!1,-1===(t=this.root._animations.indexOf(this))&<("Animation was not found"),this.root._animations.splice(t,1)}};function ke(t,e,n){var a,r,i,o,s,p,u,c,l,d,f,h,m,g;if(a=new Xt(function(t){return r=t}),"object"!=typeof t)return(n=n||{}).complete&&a.then(n.complete),n.complete=r,o=Ce(this,t,e,n),a.stop=function(){return o.stop()},a;for(i in p=(n=e||{}).easing,u=n.duration,s=[],c=n.step,l=n.complete,(c||l)&&(f={},n.step=null,n.complete=null,d=function(n){return function(t,e){f[n]=e}}),t)t.hasOwnProperty(i)&&((c||l)&&(h=d(i),n={easing:p,duration:u},c&&(n.step=h)),n.complete=l?h:B,s.push(Ce(this,i,t[i],n)));return g={easing:p,duration:u},c&&(g.step=function(t){return c(t,f)}),l&&a.then(function(t){return l(t,f)}),g.complete=r,m=Ce(this,null,null,g),s.push(m),a.stop=function(){for(var t;t=s.pop();)t.stop();m&&m.stop()},a}var Se=ye,Ee={stop:B};function Ce(t,e,n,a){var r,i,o,s;return null!==(e=e&&Pt(Tt(e)))&&(s=t.viewmodel.get(e)),we.abort(e,t),Z(s,n)?(a.complete&&a.complete(a.to),Ee):(a.easing&&"function"!=typeof(r="function"==typeof a.easing?a.easing:t.easing[a.easing])&&(r=null),i=a.duration===undefined?400:a.duration,o=new Se({keypath:e,from:s,to:n,root:t,duration:i,easing:r,interpolator:a.interpolator,step:a.step,complete:a.complete}),we.add(o),t._animations.push(o),o)}function Pe(){return this.detached||(this.el&&Wt(this.el.__ractive_instances__,this),this.detached=this.fragment.detach(),Ae.fire(this)),this.detached}var Ae=new Bt("detach");function Oe(t){return this.el?this.fragment.find(t):null}function Te(t,e){if(this._isComponentQuery?!this.selector||t.name===this.selector:t.node?p(t.node,this.selector):null)return this.push(t.node||t.instance),e||this._makeDirty(),!0}function Re(){var t,e,n;t=this._root[this._isComponentQuery?"liveComponentQueries":"liveQueries"],e=this.selector,-1!==(n=t.indexOf(e))&&(t.splice(n,1),t[e]=null)}function Me(t,e){var n,a,r,i,o,s,p,u;for(n=je(t.component||t._ractive.proxy),a=je(e.component||e._ractive.proxy),r=zt(n),i=zt(a);r&&r===i;)n.pop(),a.pop(),o=r,r=zt(n),i=zt(a);if(r=r.component||r,i=i.component||i,(p=r.parentFragment)===(u=i.parentFragment))return p.items.indexOf(r)-u.items.indexOf(i)||n.length-a.length;if(s=o.fragments)return s.indexOf(p)-s.indexOf(u)||n.length-a.length;throw new Error("An unexpected condition was met while comparing the position of two components. Please file an issue at https://github.com/RactiveJS/Ractive/issues - thanks!")}function Le(t){var e;return(e=t.parentFragment)?e.owner:t.component&&(e=t.component.parentFragment)?e.owner:void 0}function je(t){var e,n;for(e=[t],n=Le(t);n;)e.push(n),n=Le(n);return e}function De(t,e){return t.compareDocumentPosition?2&t.compareDocumentPosition(e)?1:-1:Me(t,e)}function Ne(){this.sort(this._isComponentQuery?Me:De),this._dirty=!1}function Fe(){var t=this;this._dirty||(this._dirty=!0,ge.scheduleTask(function(){t._sort()}))}function Ie(t){var e=this.indexOf(this._isComponentQuery?t.instance:t);-1!==e&&this.splice(e,1)}var Be=function Cc(t,e,n,a){var r=[];return W(r,{selector:{value:e},live:{value:n},_isComponentQuery:{value:a},_test:{value:Te}}),n&&W(r,{cancel:{value:Re},_root:{value:t},_sort:{value:Ne},_makeDirty:{value:Fe},_remove:{value:Ie},_dirty:{value:!1,writable:!0}}),r};function Ve(t,e){var n,a;return this.el?(e=e||{},n=this._liveQueries,(a=n[t])?e&&e.live?a:a.slice():((a=Be(this,t,!!e.live,!1)).live&&(n.push(t),n["_"+t]=a),this.fragment.findAll(t,a),a)):[]}function Ue(t,e){var n,a;return e=e||{},n=this._liveComponentQueries,(a=n[t])?e&&e.live?a:a.slice():((a=Be(this,t,!!e.live,!0)).live&&(n.push(t),n["_"+t]=a),this.fragment.findAllComponents(t,a),a)}function qe(t){return this.fragment.findComponent(t)}function Ge(t){return this.container?this.container.component&&this.container.component.name===t?this.container:this.container.findContainer(t):null}function ze(t){return this.parent?this.parent.component&&this.parent.component.name===t?this.parent:this.parent.findParent(t):null}var We={enqueue:function(t,e){t.event&&(t._eventQueue=t._eventQueue||[],t._eventQueue.push(t.event)),t.event=e},dequeue:function(t){t._eventQueue&&t._eventQueue.length?t.event=t._eventQueue.pop():delete t.event}},He=function Pc(t,e){var n=arguments[2]===undefined?{}:arguments[2];if(!e)return;n.event?n.event.name=e:n.event={name:e,_noArg:!0};var a=Pt(e).wildcardMatches();!function u(t,e,n,a){var r=arguments[4]!==undefined&&arguments[4];var i,o,s=!0;We.enqueue(t,n);for(o=e.length;0<=o;o--)(i=t._subs[e[o]])&&(s=Ke(t,i,n,a)&&s);We.dequeue(t);if(t.parent&&s){if(r&&t.component){var p=t.component.name+"."+e[e.length-1];e=Pt(p).wildcardMatches(),n&&(n.component=t)}u(t.parent,e,n,a)}}(t,a,n.event,n.args,!0)};function Ke(t,e,n,a){var r=null,i=!1;n&&!n._noArg&&(a=[n].concat(a));for(var o=0,s=(e=e.slice()).length;o\~:]))+)((?::[^\s\+\>\~\(]+(?:\([^\)]+\))?)?\s*[\s\+\>\~]?)\s*/g,Fn=/^@media/,In=/\[data-ractive-css~="\{[a-z0-9-]+\}"]/g;function Bn(t){return t.trim?t.trim():t.replace(/^\s+/,"").replace(/\s+$/,"")}function Vn(t){return t.str}var Un=1,qn={name:"css",extend:function(t,e,n){if(n.css){var a=Un++,r=n.noCssTransform?n.css:Ln(n.css,a);e.cssId=a,On.add({id:a,styles:r})}},init:function(){}};var Gn={name:"data",extend:function(t,e,n){var a=undefined,r=undefined;if(n.data&&et(n.data))for(a in n.data)(r=n.data[a])&&"object"==typeof r&&(et(r)||X(r))&<("Passing a `data` option with object and array properties to Ractive.extend() is discouraged, as mutating them is likely to cause bugs. Consider using a data function instead:\n\n // this...\n data: function () {\n return {\n myObject: {}\n };\n })\n\n // instead of this:\n data: {\n myObject: {}\n }");e.data=zn(e.data,n.data)},init:function(t,e,n){var a=zn(t.prototype.data,n.data);return"function"==typeof a&&(a=a.call(e)),a||{}},reset:function(t){var e=this.init(t.constructor,t,t.viewmodel);return t.viewmodel.reset(e),!0}};function zn(t,e){!function r(t){t&&t.constructor!==Object&&("function"==typeof t||("object"!=typeof t?pt("data option must be an object or a function, `"+t+"` is not valid"):lt("If supplied, options.data should be a plain JavaScript object - using a non-POJO as the root object may work, but is discouraged")))}(e);var n="function"==typeof t,a="function"==typeof e;return e||n||(e={}),n||a?function(){return Hn(a?Wn(e,this):e,n?Wn(t,this):t)}:Hn(e,t)}function Wn(t,e){var n=t.call(e);if(n)return"object"!=typeof n&&pt("Data function must return an object"),n.constructor!==Object&&dt("Data function returned something other than a plain JavaScript object. This might work, but is strongly discouraged"),n}function Hn(t,e){if(t&&e){for(var n in e)n in t||(t[n]=e[n]);return t}return t||e}var Kn=null,Qn=["preserveWhitespace","sanitize","stripComments","delimiters","tripleDelimiters","interpolate"],Yn={fromId:function Mc(t,e){var n;if(!a){if(e&&e.noThrow)return;throw new Error("Cannot retrieve template #"+t+" as Ractive is not running in a browser.")}Xn(t)&&(t=t.substring(1));if(!(n=document.getElementById(t))){if(e&&e.noThrow)return;throw new Error("Could not find template element with id #"+t)}if("SCRIPT"===n.tagName.toUpperCase())return"textContent"in n?n.textContent:n.innerHTML;if(e&&e.noThrow)return;throw new Error("Template element with id #"+t+", must be a