diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 1a7e6849d0..91e1330316 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -394,4 +394,4 @@ min_val = 0 /datum/config_entry/string/default_view - config_entry_value = "15x15" \ No newline at end of file + config_entry_value = "15x15" diff --git a/code/controllers/subsystem/processing/traits.dm b/code/controllers/subsystem/processing/traits.dm index 9d536c8131..17eae4bda2 100644 --- a/code/controllers/subsystem/processing/traits.dm +++ b/code/controllers/subsystem/processing/traits.dm @@ -9,7 +9,7 @@ PROCESSING_SUBSYSTEM_DEF(traits) wait = 10 runlevels = RUNLEVEL_GAME - var/list/traits = list() //Assoc. list of all roundstart trait datums; "name" = /path/ + var/list/traits = list() //Assoc. list of all roundstart trait datum types; "name" = /path/ var/list/trait_points = list() //Assoc. list of trait names and their "point cost"; positive numbers are good traits, and negative ones are bad var/list/trait_objects = list() //A list of all trait objects in the game, since some may process @@ -24,11 +24,10 @@ PROCESSING_SUBSYSTEM_DEF(traits) traits[initial(T.name)] = T trait_points[initial(T.name)] = initial(T.value) -/datum/controller/subsystem/processing/traits/proc/AssignTraits(mob/living/user, client/cli) - if(!isnewplayer(user)) - GenerateTraits(cli) +/datum/controller/subsystem/processing/traits/proc/AssignTraits(mob/living/user, client/cli, spawn_effects) + GenerateTraits(cli) for(var/V in cli.prefs.character_traits) - user.add_trait_datum(V) + user.add_trait_datum(V, spawn_effects) /datum/controller/subsystem/processing/traits/proc/GenerateTraits(client/user) if(user.prefs.character_traits.len) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 851dd2e750..62e20c9854 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -387,6 +387,8 @@ SUBSYSTEM_DEF(ticker) captainless=0 if(player.mind.assigned_role != player.mind.special_role) SSjob.EquipRank(N, player.mind.assigned_role, 0) + if(CONFIG_GET(flag/roundstart_traits)) + SStraits.AssignTraits(player, N.client, TRUE) CHECK_TICK if(captainless) for(var/mob/dead/new_player/N in GLOB.player_list) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 2c46621f16..45e2b667cf 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -458,7 +458,7 @@ SUBSYSTEM_DEF(timer) if (wait >= 1 && callback && callback.object && callback.object != GLOBAL_PROC && QDELETED(callback.object)) stack_trace("addtimer called with a callback assigned to a qdeleted object") - wait = max(wait, world.tick_lag) + wait = max(wait, 0) if(wait >= INFINITY) CRASH("Attempted to create timer with INFINITY delay") diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 9561515840..f1423bcf6e 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -223,25 +223,27 @@ /datum/browser/modal/listpicker var/valueslist = list() -/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox") +/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox", width, height, slidecolor) if (!User) return var/output = {"
"} @@ -252,7 +254,7 @@ output += {""} output += {"
"} - ..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, 350, 350, src, StealFocus, Timeout) + ..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, width, height, src, StealFocus, Timeout) set_content(output) /datum/browser/modal/listpicker/Topic(href,href_list) @@ -272,30 +274,32 @@ opentime = 0 close() -/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox") +/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor) if (!istype(User)) if (istype(User, /client/)) var/client/C = User User = C.mob else return - var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype) + var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype, width, height, slidecolor) A.open() A.wait() if (A.selectedbutton) return list("button" = A.selectedbutton, "values" = A.valueslist) -/proc/input_bitfield(var/mob/User, title, bitfield, current_value) +/proc/input_bitfield(var/mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null) if (!User || !(bitfield in GLOB.bitfields)) return var/list/pickerlist = list() for (var/i in GLOB.bitfields[bitfield]) + var/can_edit = 1 + if(!isnull(allowed_edit_list) && !(allowed_edit_list & GLOB.bitfields[bitfield][i])) + can_edit = 0 if (current_value & GLOB.bitfields[bitfield][i]) - pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i)) + pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit)) else - pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i)) - var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist) - + pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit)) + var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist, width = nwidth, height = nheight, slidecolor = nslidecolor) if (islist(result)) if (result["button"] == 2) // If the user pressed the cancel button return diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index bc04a67308..c302059324 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -109,6 +109,7 @@ return ..() + //Proc to use when you 100% want to try to infect someone (ignoreing protective clothing and such), as long as they aren't immune /mob/living/proc/ForceContractDisease(datum/disease/D, make_copy = TRUE, del_on_fail = FALSE) if(!CanContractDisease(D)) diff --git a/code/datums/traits/_trait.dm b/code/datums/traits/_trait.dm index a6073b5008..b7cf589ef5 100644 --- a/code/datums/traits/_trait.dm +++ b/code/datums/traits/_trait.dm @@ -11,7 +11,7 @@ var/mob_trait //if applicable, apply and remove this mob trait var/mob/living/trait_holder -/datum/trait/New(mob/living/trait_mob) +/datum/trait/New(mob/living/trait_mob, spawn_effects) ..() if(!trait_mob || (human_only && !ishuman(trait_mob)) || trait_mob.has_trait_datum(type)) qdel(src) @@ -23,9 +23,9 @@ trait_holder.add_trait(mob_trait, ROUNDSTART_TRAIT) START_PROCESSING(SStraits, src) add() - if(!SSticker.HasRoundStarted()) //on roundstart or on latejoin; latejoin code is in new_player.dm + if(spawn_effects) on_spawn() - addtimer(CALLBACK(src, .proc/post_add), 30) + addtimer(CALLBACK(src, .proc/post_add), 30) /datum/trait/Destroy() STOP_PROCESSING(SStraits, src) @@ -38,16 +38,25 @@ SStraits.trait_objects -= src return ..() +/datum/trait/proc/transfer_mob(mob/living/to_mob) + trait_holder.roundstart_traits -= src + to_mob.roundstart_traits += src + trait_holder = to_mob + on_transfer() + /datum/trait/proc/add() //special "on add" effects /datum/trait/proc/on_spawn() //these should only trigger when the character is being created for the first time, i.e. roundstart/latejoin /datum/trait/proc/remove() //special "on remove" effects /datum/trait/proc/on_process() //process() has some special checks, so this is the actual process /datum/trait/proc/post_add() //for text, disclaimers etc. given after you spawn in with the trait +/datum/trait/proc/on_transfer() //code called when the trait is transferred to a new mob /datum/trait/process() if(QDELETED(trait_holder)) qdel(src) return + if(trait_holder.stat == DEAD) + return on_process() /mob/living/proc/get_trait_string(medical) //helper string. gets a string of all the traits the mob has @@ -67,6 +76,16 @@ return "None" return dat.Join("
") +/mob/living/proc/cleanse_trait_datums() //removes all trait datums + for(var/V in roundstart_traits) + var/datum/trait/T = V + qdel(T) + +/mob/living/proc/transfer_trait_datums(mob/living/to_mob) + for(var/V in roundstart_traits) + var/datum/trait/T = V + T.transfer_mob(to_mob) + /* Commented version of Nearsighted to help you add your own traits diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 9fd966eead..c98db62e8b 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -114,7 +114,7 @@ if(trait_holder.reagents.has_reagent("mindbreaker")) trait_holder.hallucination = 0 return - if(prob(1)) //we'll all be mad soon enough + if(prob(2)) //we'll all be mad soon enough madness() /datum/trait/insanity/proc/madness(mad_fools) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 8738ac96a9..50ad92d44b 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -542,4 +542,4 @@ if(EMERGENCY_ESCAPED_OR_ENDGAMED) SSticker.news_report = STATION_EVACUATED if(SSshuttle.emergency.is_hijacked()) - SSticker.news_report = SHUTTLE_HIJACK \ No newline at end of file + SSticker.news_report = SHUTTLE_HIJACK diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 048cac8326..da253551e0 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -126,7 +126,7 @@ return examine(user) //Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions) +/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/traits) if(panel_open) return FALSE if(mess || attempting) @@ -198,6 +198,9 @@ if(H) H.faction |= factions + for(var/V in traits) + new V(H) + H.set_cloned_appearance() H.suiciding = FALSE @@ -316,6 +319,7 @@ SPEAK("An emergency ejection of [clonemind.name] has occurred. Survival not guaranteed.") to_chat(user, "You force an emergency ejection. ") go_out() + mob_occupant.apply_vore_prefs() else return ..() @@ -398,6 +402,7 @@ /obj/machinery/clonepod/container_resist(mob/living/user) if(user.stat == CONSCIOUS) go_out() + mob_occupant.apply_vore_prefs() /obj/machinery/clonepod/emp_act(severity) var/mob/living/mob_occupant = occupant @@ -405,6 +410,7 @@ connected_message(Gibberish("EMP-caused Accidental Ejection", 0)) SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of [mob_occupant.real_name] prematurely." ,0)) go_out() + mob_occupant.apply_vore_prefs() ..() /obj/machinery/clonepod/ex_act(severity, target) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index d6cf184622..40f003ea34 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -71,7 +71,7 @@ if(pod.occupant) continue //how though? - if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"])) + if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["traits"])) temp = "[R.fields["name"]] => Cloning cycle in progress..." records -= R @@ -409,7 +409,7 @@ else if(pod.occupant) temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"])) + else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["traits"])) temp = "[C.fields["name"]] => Cloning cycle in progress..." playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) records.Remove(C) @@ -482,6 +482,10 @@ R.fields["blood_type"] = dna.blood_type R.fields["features"] = dna.features R.fields["factions"] = mob_occupant.faction + R.fields["traits"] = list() + for(var/V in mob_occupant.roundstart_traits) + var/datum/trait/T = V + R.fields["traits"] += T.type if (!isnull(mob_occupant.mind)) //Save that mind so traitors can continue traitoring after cloning. R.fields["mind"] = "[REF(mob_occupant.mind)]" diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 794cdf5b30..356c21d9f3 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -75,4 +75,4 @@ return 0 if(B.scrambledcodes || B.emagged) return 0 - return ..() + return ..() \ No newline at end of file diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 83b58fde65..609da46c52 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -101,8 +101,6 @@ /obj/machinery/door/airlock/Initialize() . = ..() wires = new /datum/wires/airlock(src) - if (cyclelinkeddir) - cyclelinkairlock() if(frequency) set_frequency(frequency) @@ -127,6 +125,8 @@ /obj/machinery/door/airlock/LateInitialize() . = ..() + if (cyclelinkeddir) + cyclelinkairlock() if(abandoned) var/outcome = rand(1,100) switch(outcome) @@ -178,6 +178,7 @@ limit-- while(!FoundDoor && limit) if (!FoundDoor) + log_world("### MAP WARNING, [src] at [get_area_name(src, TRUE)] [COORD(src)] failed to find a valid airlock to cyclelink with!") return FoundDoor.cyclelinkedairlock = src cyclelinkedairlock = FoundDoor diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 2bce3671e0..9e467e8926 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -68,4 +68,4 @@ /obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user) if(stat & NOPOWER) - open(1) \ No newline at end of file + open(1) diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index 239bbd5c60..1846eea322 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -178,3 +178,4 @@ priority = "Extreme" else priority = "Undetermined" + diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index caaa3e3a00..7896d7aa35 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -19,3 +19,4 @@ /obj/mecha/combat/durand/RemoveActions(mob/living/user, human_occupant = 0) ..() defense_action.Remove(user) + diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 15b865c1e9..f5f369c2ad 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -27,3 +27,4 @@ ..() switch_damtype_action.Remove(user) phasing_action.Remove(user) + diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index f5d217f0b0..fc2d9f1e3f 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -338,7 +338,7 @@ update_label("John Doe", "Clowny") /obj/item/card/id/mining name = "mining ID" - access = list(ACCESS_MINERAL_STOREROOM) // CITADEL CHANGE removes golem's ability to get on the station willy nilly. + access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) /obj/item/card/id/away name = "a perfectly generic identification card" diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 5fc4bf7068..95aaa85b70 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -547,7 +547,6 @@ /obj/item/circuitboard/machine/tesla_coil/Initialize() . = ..() if(build_path) - name = "Tesla Coil (Machine Board)" build_path = PATH_POWERCOIL /obj/item/circuitboard/machine/tesla_coil/attackby(obj/item/I, mob/user, params) diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 800ca21d03..e3194cd1a7 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -648,4 +648,4 @@ item_state = "defibpaddles0" req_defib = FALSE -#undef HALFWAYCRITDEATH \ No newline at end of file +#undef HALFWAYCRITDEATH diff --git a/code/game/objects/items/hot_potato.dm b/code/game/objects/items/hot_potato.dm index 418026aeef..65bfd09f9c 100644 --- a/code/game/objects/items/hot_potato.dm +++ b/code/game/objects/items/hot_potato.dm @@ -1,4 +1,3 @@ - //CREATOR'S NOTE: DO NOT FUCKING GIVE THIS TO BOTANY! /obj/item/hot_potato name = "hot potato" diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 112caf26c0..ca1e18af1b 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -192,6 +192,14 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/glass/beaker( src ) +/obj/item/storage/box/medsprays + name = "box of medical sprayers" + desc = "A box full of medical sprayers, with unscrewable caps and precision spray heads." + +/obj/item/storage/box/medsprays/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/medspray( src ) + /obj/item/storage/box/injectors name = "box of DNA injectors" desc = "This box contains injectors, it seems." diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 61b44d658e..6bae2af476 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -357,5 +357,5 @@ if(get_fuel() < max_fuel && nextrefueltick < world.time) nextrefueltick = world.time + 10 reagents.add_reagent("welding_fuel", 1) - -#undef WELDER_FUEL_BURN_INTERVAL + +#undef WELDER_FUEL_BURN_INTERVAL \ No newline at end of file diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index cd632032ac..34cdbd646b 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -42,6 +42,7 @@ icon_state = "waterballoon-e" item_state = "balloon-empty" + /obj/item/toy/balloon/New() create_reagents(10) ..() @@ -286,6 +287,7 @@ w_class = WEIGHT_CLASS_SMALL resistance_flags = FLAMMABLE + /obj/item/toy/windupToolbox name = "windup toolbox" desc = "A replica toolbox that rumbles when you turn the key." @@ -332,7 +334,7 @@ /obj/item/toy/katana name = "replica katana" - desc = "Woefully underpowered in D20. Almost has a sharp edge." + desc = "Woefully underpowered in D20." icon = 'icons/obj/items_and_weapons.dmi' icon_state = "katana" item_state = "katana" diff --git a/code/game/objects/structures/crates_lockers/closets/fitness.dm b/code/game/objects/structures/crates_lockers/closets/fitness.dm index 2a17e0038c..ad493dd6f5 100644 --- a/code/game/objects/structures/crates_lockers/closets/fitness.dm +++ b/code/game/objects/structures/crates_lockers/closets/fitness.dm @@ -62,4 +62,4 @@ new /obj/item/gun/energy/laser/bluetag(src) for(var/i in 1 to 3) new /obj/item/clothing/suit/bluetag(src) - new /obj/item/clothing/head/helmet/bluetaghelm(src) \ No newline at end of file + new /obj/item/clothing/head/helmet/bluetaghelm(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index fa9d9e9fd2..d01d4ccaab 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -96,3 +96,5 @@ ..() new /obj/item/storage/box/pillbottles(src) new /obj/item/storage/box/pillbottles(src) + new /obj/item/storage/box/medsprays(src) + new /obj/item/storage/box/medsprays(src) \ No newline at end of file diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 73f3c1e451..73fc0d43f3 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -39,7 +39,7 @@ return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect()) log_access("Failed Login: [key] - Guests not allowed during panic bunker") - return list("reason"="guest", "desc"="\nReason: You must first join the Discord to verify your account before joining this server. Please ping an admin once you've joined and read the rules. https://discord.gg/E6SQuhz") + return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.") //Population Cap Checking var/extreme_popcap = CONFIG_GET(number/extreme_popcap) diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index c915816ed0..e4b552b6b5 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -1,13 +1,17 @@ GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums GLOBAL_PROTECT(admin_ranks) +GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt +GLOBAL_PROTECT(protected_ranks) + /datum/admin_rank var/name = "NoRank" var/rights = R_DEFAULT - var/list/adds - var/list/subs + var/exclude_rights = 0 + var/include_rights = 0 + var/can_edit_rights = 0 -/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs) +/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" message_admins("[key_name_admin(usr)][msg]") @@ -17,19 +21,18 @@ GLOBAL_PROTECT(admin_ranks) CRASH("Admin proc call creation of admin datum") return name = init_name - switch(name) - if("Removed",null,"") - QDEL_IN(src, 0) - throw EXCEPTION("invalid admin-rank name") - return + if(!name) + qdel(src) + throw EXCEPTION("Admin rank created without name.") + return if(init_rights) rights = init_rights - if(!init_adds) - init_adds = list() - if(!init_subs) - init_subs = list() - adds = init_adds - subs = init_subs + include_rights = rights + if(init_exclude_rights) + exclude_rights = init_exclude_rights + rights &= ~exclude_rights + if(init_edit_rights) + can_edit_rights = init_edit_rights /datum/admin_rank/Destroy() if(IsAdminAdvancedProcCall()) @@ -75,13 +78,12 @@ GLOBAL_PROTECT(admin_ranks) flag = R_SPAWN if("autologin", "autoadmin") flag = R_AUTOLOGIN + if("dbranks") + flag = R_DBRANKS if("@","prev") flag = previous_rights return flag -/proc/admin_keyword_to_path(word) //use this with verb keywords eg +/client/proc/blah - return text2path(copytext(word, 2, findtext(word, " ", 2, 0))) - // Adds/removes rights to this admin_rank /datum/admin_rank/proc/process_keyword(word, previous_rights=0) if(IsAdminAdvancedProcCall()) @@ -94,157 +96,156 @@ GLOBAL_PROTECT(admin_ranks) switch(text2ascii(word,1)) if(43) rights |= flag //+ + include_rights |= flag if(45) rights &= ~flag //- - else - //isn't a keyword so maybe it's a verbpath? - var/path = admin_keyword_to_path(word) - if(path) - switch(text2ascii(word,1)) - if(43) - if(!subs.Remove(path)) - adds += path //+ - if(45) - if(!adds.Remove(path)) - subs += path //- - + exclude_rights |= flag + if(42) + can_edit_rights |= flag //* // Checks for (keyword-formatted) rights on this admin /datum/admins/proc/check_keyword(word) var/flag = admin_keyword_to_flag(word) if(flag) return ((rank.rights & flag) == flag) //true only if right has everything in flag - else - var/path = admin_keyword_to_path(word) - for(var/i in owner.verbs) //this needs to be a foreach loop for some reason. in operator and verbs.Find() don't work - if(i == path) - return 1 - return 0 //load our rank - > rights associations -/proc/load_admin_ranks() +/proc/load_admin_ranks(dbfail) if(IsAdminAdvancedProcCall()) to_chat(usr, "Admin Reload blocked: Advanced ProcCall detected.") return GLOB.admin_ranks.Cut() - - if(CONFIG_GET(flag/admin_legacy_system)) - var/previous_rights = 0 - //load text from file and process each line separately - for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt")) - if(!line) - continue - if(findtextEx(line,"#",1,2)) - continue - - var/next = findtext(line, "=") - var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next))) - if(!R) - continue - GLOB.admin_ranks += R - - var/prev = findchar(line, "+-", next, 0) - while(prev) - next = findchar(line, "+-", prev + 1, 0) - R.process_keyword(copytext(line, prev, next), previous_rights) - prev = next - - previous_rights = R.rights - else - if(!SSdbcore.Connect()) - if(CONFIG_GET(flag/sql_enabled)) - var/msg = "Failed to connect to database in load_admin_ranks(). Reverting to legacy system." - log_world(msg) - WRITE_FILE(GLOB.world_game_log, msg) - CONFIG_SET(flag/admin_legacy_system, TRUE) - load_admin_ranks() - return - - var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags FROM [format_table_name("admin_ranks")]") + GLOB.protected_ranks.Cut() + var/previous_rights = 0 + //load text from file and process each line separately + for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt")) + if(!line || findtextEx(line,"#",1,2)) + continue + var/next = findtext(line, "=") + var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next))) + if(!R) + continue + GLOB.admin_ranks += R + GLOB.protected_ranks += R + var/prev = findchar(line, "+-*", next, 0) + while(prev) + next = findchar(line, "+-*", prev + 1, 0) + R.process_keyword(copytext(line, prev, next), previous_rights) + prev = next + previous_rights = R.rights + if(!CONFIG_GET(flag/admin_legacy_system) || dbfail) + var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]") if(!query_load_admin_ranks.Execute()) + message_admins("Error loading admin ranks from database. Loading from backup.") + log_sql("Error loading admin ranks from database. Loading from backup.") + dbfail = 1 + else + while(query_load_admin_ranks.NextRow()) + var/skip + var/rank_name = query_load_admin_ranks.item[1] + for(var/datum/admin_rank/R in GLOB.admin_ranks) + if(R.name == rank_name) //this rank was already loaded from txt override + skip = 1 + break + if(!skip) + var/rank_flags = text2num(query_load_admin_ranks.item[2]) + var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3]) + var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4]) + var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags) + if(!R) + continue + GLOB.admin_ranks += R + //load ranks from backup file + if(dbfail) + var/backup_file = file("data/admins_backup.json") + if(!fexists(backup_file)) + log_world("Unable to locate admins backup file.") return - while(query_load_admin_ranks.NextRow()) - var/rank_name = ckeyEx(query_load_admin_ranks.item[1]) - var/flags = query_load_admin_ranks.item[2] - if(istext(flags)) - flags = text2num(flags) - var/datum/admin_rank/R = new(rank_name, flags) + var/list/json = json_decode(file2text(backup_file)) + for(var/J in json["ranks"]) + for(var/datum/admin_rank/R in GLOB.admin_ranks) + if(R.name == "[J]") //this rank was already loaded from txt override + continue + var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"]) if(!R) continue GLOB.admin_ranks += R - + return 1 #ifdef TESTING var/msg = "Permission Sets Built:\n" for(var/datum/admin_rank/R in GLOB.admin_ranks) msg += "\t[R.name]" - var/rights = rights2text(R.rights,"\n\t\t",R.adds,R.subs) + var/rights = rights2text(R.rights,"\n\t\t") if(rights) msg += "\t\t[rights]\n" testing(msg) #endif - /proc/load_admins() + var/dbfail + if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect()) + message_admins("Failed to connect to database while loading admins. Loading from backup.") + log_sql("Failed to connect to database while loading admins. Loading from backup.") + dbfail = 1 //clear the datums references - GLOB.admin_datums.Cut() for(var/client/C in GLOB.admins) C.remove_admin_verbs() C.holder = null GLOB.admins.Cut() + GLOB.protected_admins.Cut() GLOB.deadmins.Cut() - load_admin_ranks() + dbfail = load_admin_ranks(dbfail) //Clear profile access for(var/A in world.GetConfig("admin")) world.SetConfig("APP/admin", A, null) - var/list/rank_names = list() for(var/datum/admin_rank/R in GLOB.admin_ranks) rank_names[R.name] = R - - if(CONFIG_GET(flag/admin_legacy_system)) - //load text from file - var/list/lines = world.file2list("[global.config.directory]/admins.txt") - - //process each line separately - for(var/line in lines) - if(!length(line)) - continue - if(findtextEx(line, "#", 1, 2)) - continue - - var/list/entry = splittext(line, "=") - if(entry.len < 2) - continue - - var/ckey = ckey(entry[1]) - var/rank = ckeyEx(entry[2]) - if(!ckey || !rank) - continue - - new /datum/admins(rank_names[rank], ckey) - - else - if(!SSdbcore.Connect()) - log_world("Failed to connect to database in load_admins(). Reverting to legacy system.") - WRITE_FILE(GLOB.world_game_log, "Failed to connect to database in load_admins(). Reverting to legacy system.") - CONFIG_SET(flag/admin_legacy_system, TRUE) - load_admins() - return - + //ckeys listed in admins.txt are always made admins before sql loading is attempted + var/list/lines = world.file2list("[global.config.directory]/admins.txt") + for(var/line in lines) + if(!length(line) || findtextEx(line, "#", 1, 2)) + continue + var/list/entry = splittext(line, "=") + if(entry.len < 2) + continue + var/ckey = ckey(entry[1]) + var/rank = ckeyEx(entry[2]) + if(!ckey || !rank) + continue + new /datum/admins(rank_names[rank], ckey, 0, 1) + if(!CONFIG_GET(flag/admin_legacy_system) || dbfail) var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]") if(!query_load_admins.Execute()) + message_admins("Error loading admins from database. Loading from backup.") + log_sql("Error loading admins from database. Loading from backup.") + dbfail = 1 + else + while(query_load_admins.NextRow()) + var/admin_ckey = query_load_admins.item[1] + var/admin_rank = query_load_admins.item[2] + var/skip + if(rank_names[admin_rank] == null) + message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].") + log_sql("[admin_ckey] loaded with invalid admin rank [admin_rank].") + skip = 1 + if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey]) + skip = 1 + if(!skip) + new /datum/admins(rank_names[admin_rank], admin_ckey) + //load admins from backup file + if(dbfail) + var/backup_file = file("data/admins_backup.json") + if(!fexists(backup_file)) + log_world("Unable to locate admins backup file.") return - while(query_load_admins.NextRow()) - var/ckey = ckey(query_load_admins.item[1]) - var/rank = ckeyEx(query_load_admins.item[2]) - - if(rank_names[rank] == null) - WARNING("Admin rank ([rank]) does not exist.") - continue - - new /datum/admins(rank_names[rank], ckey) - + var/list/json = json_decode(file2text(backup_file)) + for(var/J in json["admins"]) + for(var/A in GLOB.admin_datums + GLOB.deadmins) + if(A == "[J]") //this admin was already loaded from txt override + continue + new /datum/admins(rank_names[json["admins"]["[J]"]], "[J]") #ifdef TESTING var/msg = "Admins Built:\n" for(var/ckey in GLOB.admin_datums) @@ -252,7 +253,7 @@ GLOBAL_PROTECT(admin_ranks) msg += "\t[ckey] - [D.rank.name]\n" testing(msg) #endif - + return dbfail #ifdef TESTING /client/verb/changerank(newrank in GLOB.admin_ranks) @@ -271,149 +272,3 @@ GLOBAL_PROTECT(admin_ranks) remove_admin_verbs() holder.associate(src) #endif - -/datum/admins/proc/edit_rights_topic(list/href_list) - if(!check_rights(R_PERMISSIONS)) - message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") - log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") - return - if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.") - return - - var/adm_ckey - var/task = href_list["editrights"] - switch(task) - if("add") - var/new_ckey = ckey(input(usr,"New admin's ckey","Admin ckey", null) as text|null) - if(!new_ckey) - return - if(new_ckey in GLOB.admin_datums) - to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") - return - adm_ckey = new_ckey - task = "rank" - else - adm_ckey = ckey(href_list["ckey"]) - if(!adm_ckey) - to_chat(usr, "Error: Topic 'editrights': No valid ckey") - return - - var/datum/admins/D = GLOB.admin_datums[adm_ckey] - if (!D) - D = GLOB.deadmins[adm_ckey] - - switch(task) - if("remove") - if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") - if(!D) - return - if(!check_if_greater_rights_than_holder(D)) - message_admins("[key_name_admin(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.") - log_admin("[key_name(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.") - return - GLOB.admin_datums -= adm_ckey - GLOB.deadmins -= adm_ckey - D.disassociate() - - updateranktodb(adm_ckey, "player") - message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") - log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") - log_admin_rank_modification(adm_ckey, "Removed") - - if("rank") - var/datum/admin_rank/R - - var/list/rank_names = list("*New Rank*") - for(R in GLOB.admin_ranks) - rank_names[R.name] = R - - var/new_rank = input("Please select a rank", "New rank", null, null) as null|anything in rank_names - - switch(new_rank) - if(null) - return - if("*New Rank*") - new_rank = ckeyEx(input("Please input a new rank", "New custom rank", null, null) as null|text) - if(!new_rank) - return - - if(D) - if(!check_if_greater_rights_than_holder(D)) - message_admins("[key_name_admin(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.") - log_admin("[key_name(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.") - return - - R = rank_names[new_rank] - if(!R) //rank with that name doesn't exist yet - make it - if(D) - R = new(new_rank, D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name - else - R = new(new_rank) //blank new admin_rank - GLOB.admin_ranks += R - - if(D) //they were previously an admin - D.disassociate() //existing admin needs to be disassociated - D.rank = R //set the admin_rank as our rank - D.associate() - else - D = new(R, adm_ckey, TRUE) //new admin - - updateranktodb(adm_ckey, new_rank) - message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin_rank_modification(adm_ckey, new_rank) - - if("permissions") - if(!D) - return //they're not an admin! - - var/keyword = input("Input permission keyword (one at a time):\ne.g. +BAN or -FUN or +/client/proc/someverb", "Permission toggle", null, null) as null|text - if(!keyword) - return - - if(!check_keyword(keyword) || !check_if_greater_rights_than_holder(D)) - message_admins("[key_name_admin(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.") - log_admin("[key_name(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.") - return - - D.disassociate() - - if(!findtext(D.rank.name, "([adm_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too - D.rank = new("[D.rank.name]([adm_ckey])", D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name - //we don't add this clone to the admin_ranks list, as it is unique to that ckey - D.rank.process_keyword(keyword) - - var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) - D.associate(C) //link up with the client and add verbs - - message_admins("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]") - log_admin("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]") - log_admin_permission_modification(adm_ckey, D.rank.rights) - if("activate") //forcefully readmin - if(!D || !D.deadmined) - return - - D.activate() - - message_admins("[key_name_admin(usr)] forcefully readmined [adm_ckey]") - log_admin("[key_name(usr)] forcefully readmined [adm_ckey]") - if("deactivate") //forcefully deadmin - if(!D || D.deadmined) - return - - message_admins("[key_name_admin(usr)] forcefully deadmined [adm_ckey]") - log_admin("[key_name(usr)] forcefully deadmined [adm_ckey]") - - D.deactivate() //after logs so the deadmined admin can see the message. - - edit_admin_permissions() - -/datum/admins/proc/updateranktodb(ckey,newrank) - if(!SSdbcore.Connect()) - return - var/sql_ckey = sanitizeSQL(ckey) - var/sql_admin_rank = sanitizeSQL(newrank) - - var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") - query_admin_rank_update.Execute() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 85811f693c..695d6775a5 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -18,7 +18,6 @@ GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault()) /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_ticket_panel, - /client/proc/panicbunker, /client/proc/stop_sounds ) GLOBAL_PROTECT(admin_verbs_admin) @@ -117,6 +116,7 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer()) /client/proc/toggle_random_events, /client/proc/forcerandomrotate, /client/proc/adminchangemap, + /client/proc/panicbunker, /client/proc/toggle_hub ) GLOBAL_PROTECT(admin_verbs_debug) @@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug()) /client/proc/pump_random_event, /client/proc/cmd_display_init_log, /client/proc/cmd_display_overlay_log, - /datum/admins/proc/create_or_modify_area + /datum/admins/proc/create_or_modify_area, ) GLOBAL_PROTECT(admin_verbs_possess) GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release)) @@ -267,11 +267,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( if(rights & R_SPAWN) verbs += GLOB.admin_verbs_spawn - for(var/path in holder.rank.adds) - verbs += path - for(var/path in holder.rank.subs) - verbs -= path - /client/proc/remove_admin_verbs() verbs.Remove( GLOB.admin_verbs_default, @@ -306,8 +301,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /client/proc/cmd_admin_areatest_station, /client/proc/readmin ) - if(holder) - verbs.Remove(holder.rank.adds) /client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs set name = "Adminverbs - Hide Most" @@ -528,8 +521,10 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( set desc = "Get the estimated range of a bomb, using explosive power." var/ex_power = input("Explosive Power:") as null|num + if (isnull(ex_power)) + return var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE) - to_chat(usr, "Estimated Explosive Range: (Devestation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])") + to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])") /client/proc/get_dynex_power() set category = "Debug" @@ -537,6 +532,8 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( set desc = "Get the estimated required power of a bomb, to reach a specific range." var/ex_range = input("Light Explosion Range:") as null|num + if (isnull(ex_range)) + return var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE) to_chat(usr, "Estimated Explosive Power: [power]") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 59d432574d..321142376a 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,5 +1,7 @@ GLOBAL_LIST_EMPTY(admin_datums) GLOBAL_PROTECT(admin_datums) +GLOBAL_LIST_EMPTY(protected_admins) +GLOBAL_PROTECT(protected_admins) GLOBAL_VAR_INIT(href_token, GenerateToken()) GLOBAL_PROTECT(href_token) @@ -26,7 +28,7 @@ GLOBAL_PROTECT(href_token) var/deadmined -/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE) +/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" message_admins("[key_name_admin(usr)][msg]") @@ -51,6 +53,8 @@ GLOBAL_PROTECT(href_token) if(R.rights & R_DEBUG) //grant profile access world.SetConfig("APP/admin", ckey, "role=admin") //only admins with +ADMIN start admined + if(protected) + GLOB.protected_admins[target] = src if (force_active || (R.rights & R_AUTOLOGIN)) activate() else diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm new file mode 100644 index 0000000000..1173c9393c --- /dev/null +++ b/code/modules/admin/permissionedit.dm @@ -0,0 +1,274 @@ +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) + return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) + return + + var/list/output = list({" + + +Permissions Panel + + + + +
+ + + + + + + +"}) + + for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins) + var/datum/admins/D = GLOB.admin_datums[adm_ckey] + if(!D) + D = GLOB.deadmins[adm_ckey] + if (!D) + continue + + var/deadminlink = "" + if (D.deadmined) + deadminlink = " \[RA\]" + else + deadminlink = " \[DA\]" + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + output += {" +
CKEY \[+\]RANKPERMISSIONSDENIEDALLOWED TO EDIT
[adm_ckey] [deadminlink]\[-\][D.rank.name][rights2text(D.rank.include_rights," ")][rights2text(D.rank.exclude_rights," ", "-")][rights2text(D.rank.can_edit_rights," ", "*")]
+
Search:
+ +"} + + usr << browse(jointext(output, ""),"window=editrights;size=1000x650") + +/datum/admins/proc/edit_rights_topic(list/href_list) + if(!check_rights(R_PERMISSIONS)) + message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.") + log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.") + return + if(IsAdminAdvancedProcCall()) + to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.") + return + var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions) + permissions_assets.send(src) + var/admin_ckey = ckey(href_list["ckey"]) + var/datum/admins/D = GLOB.admin_datums[admin_ckey] + var/use_db + var/task = href_list["editrights"] + var/skip + if(task == "activate" || task == "deactivate") + skip = 1 + if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank") + if(admin_ckey in GLOB.protected_admins) + to_chat(usr, "Editing the rank of this admin is blocked by server configuration.") + return + if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions") + if(D.rank in GLOB.protected_ranks) + to_chat(usr, "Editing the flags of this rank is blocked by server configuration.") + return + if(check_rights(R_DBRANKS, 0)) + if(!skip) + if(!SSdbcore.Connect()) + to_chat(usr, "Unable to connect to database, changes are temporary only.") + use_db = "Temporary" + if(!use_db) + use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") + if(use_db == "Cancel") + return + if(use_db == "Permanent") + use_db = 1 + admin_ckey = sanitizeSQL(admin_ckey) + else + use_db = 0 + if(task != "add") + D = GLOB.admin_datums[admin_ckey] + if(!D) + D = GLOB.deadmins[admin_ckey] + if(!D) + return + if(!check_if_greater_rights_than_holder(D)) + message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.") + log_admin("[key_name(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.") + switch(task) + if("add") + admin_ckey = add_admin(use_db) + if(!admin_ckey) + return + change_admin_rank(admin_ckey, use_db) + if("remove") + remove_admin(admin_ckey, use_db, D) + if("rank") + change_admin_rank(admin_ckey, use_db, D) + if("permissions") + change_admin_flags(admin_ckey, use_db, D) + if("activate") + force_readmin(admin_ckey, D) + if("deactivate") + force_deadmin(admin_ckey, D) + edit_admin_permissions() + +/datum/admins/proc/add_admin(use_db) + . = sanitizeSQL(ckey(input("New admin's ckey","Admin ckey") as text|null)) + if(!.) + return 0 + if(. in GLOB.admin_datums+GLOB.deadmins) + to_chat(usr, "[.] is already an admin.") + return 0 + if(use_db) + var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')") + if(!query_add_admin.warn_execute()) + return 0 + var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', 'New admin added: [.]')") + if(!query_add_admin_log.warn_execute()) + return 0 + +/datum/admins/proc/remove_admin(admin_ckey, use_db, datum/admins/D) + if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it") + GLOB.admin_datums -= admin_ckey + GLOB.deadmins -= admin_ckey + D.disassociate() + if(use_db) + var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'") + if(!query_add_rank.warn_execute()) + return + var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', 'Admin removed: [admin_ckey]')") + if(!query_add_rank_log.warn_execute()) + return + message_admins("[key_name_admin(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]") + log_admin("[key_name(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]") + +/datum/admins/proc/force_readmin(admin_ckey, datum/admins/D) + if(!D || !D.deadmined) + return + D.activate() + message_admins("[key_name_admin(usr)] forcefully readmined [admin_ckey]") + log_admin("[key_name(usr)] forcefully readmined [admin_ckey]") + +/datum/admins/proc/force_deadmin(admin_ckey, datum/admins/D) + if(!D || D.deadmined) + return + message_admins("[key_name_admin(usr)] forcefully deadmined [admin_ckey]") + log_admin("[key_name(usr)] forcefully deadmined [admin_ckey]") + D.deactivate() //after logs so the deadmined admin can see the message. + +/datum/admins/proc/change_admin_rank(admin_ckey, use_db, datum/admins/D) + var/datum/admin_rank/R + var/list/rank_names = list("*New Rank*") + for(R in GLOB.admin_ranks) + if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights) + rank_names[R.name] = R + var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names + if(new_rank == "*New Rank*") + new_rank = sanitizeSQL(ckeyEx(input("Please input a new rank", "New custom rank") as text|null)) + if(!new_rank) + return + R = rank_names[new_rank] + if(!R) //rank with that name doesn't exist yet - make it + if(D) + R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name + else + R = new(new_rank) //blank new admin_rank + GLOB.admin_ranks += R + if(use_db) + if(!R) + var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_rights) VALUES ('[new_rank]', '0', '0', '0')") + if(!query_add_rank.warn_execute()) + return + var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', 'New rank added: [admin_ckey]')") + if(!query_add_rank_log.warn_execute()) + return + var/old_rank + var/datum/DBQuery/query_get_rank = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'") + if(!query_get_rank.warn_execute()) + return + if(query_get_rank.NextRow()) + old_rank = query_get_rank.item[1] + var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'") + if(!query_change_rank.warn_execute()) + return + var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')") + if(!query_change_rank_log.warn_execute()) + return + if(D) //they were previously an admin + D.disassociate() //existing admin needs to be disassociated + D.rank = R //set the admin_rank as our rank + D.associate() + else + D = new(R, admin_ckey, TRUE) //new admin + message_admins("[key_name_admin(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]") + log_admin("[key_name(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]") + +/datum/admins/proc/change_admin_flags(admin_ckey, use_db, datum/admins/D) + var/new_flags = input_bitfield(usr, "Include permission flags
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights) + if(isnull(new_flags)) + return + var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags
Flags enabled here will be removed from a rank.
Note these take precedence over included flags.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.exclude_rights, 350, 660, "red", usr.client.holder.rank.can_edit_rights) + if(isnull(new_exclude_flags)) + return + var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags
These are the flags this rank is allowed to edit if they have access to the permissions panel.
They will be unable to modify admins to a rank that has a flag not included here.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights) + if(isnull(new_can_edit_flags)) + return + if(use_db) + var/old_flags + var/old_exclude_flags + var/old_can_edit_flags + var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'") + if(!query_get_rank_flags.warn_execute()) + return + if(query_get_rank_flags.NextRow()) + old_flags = text2num(query_get_rank_flags.item[1]) + old_exclude_flags = text2num(query_get_rank_flags.item[2]) + old_can_edit_flags = text2num(query_get_rank_flags.item[3]) + var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'") + if(!query_change_rank_flags.warn_execute()) + return + var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', 'Permissions of [admin_ckey] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')") + if(!query_change_rank_flags_log.warn_execute()) + return + for(var/datum/admin_rank/R in GLOB.admin_ranks) + if(R.name != D.rank.name) + continue + R.rights = new_flags &= ~new_exclude_flags + R.exclude_rights = new_exclude_flags + R.include_rights = new_flags + R.can_edit_rights = new_can_edit_flags + for(var/i in GLOB.admin_datums+GLOB.deadmins) + var/datum/admins/A = GLOB.admin_datums[i] + if(!A) + A = GLOB.deadmins[i] + if (!A) + continue + if(A.rank.name != D.rank.name) + continue + var/client/C = GLOB.directory[A.target] + A.disassociate() + A.associate(C) + else + D.disassociate() + if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too + D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //duplicate our previous admin_rank but with a new name + //we don't add this clone to the admin_ranks list, as it is unique to that ckey + else + D.rank.rights = new_flags &= ~new_exclude_flags + D.rank.include_rights = new_flags + D.rank.exclude_rights = new_exclude_flags + var/client/C = GLOB.directory[admin_ckey] //find the client with the specified ckey (if they are logged in) + D.associate(C) //link up with the client and add verbs + message_admins("[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]") + log_admin("[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]") diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index a758d295e2..b8176a4ce7 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -31,6 +31,7 @@ Reset Thunderdome to default state
Rename Station Name
Reset Station Name
+ Set Night Shift Mode

Shuttles

@@ -54,7 +55,6 @@ Power all SMES
Triple AI mode (needs to be used in the lobby)
Everyone is the traitor
- AK-47s For Everyone!
Summon Guns
Summon Magic
Summon Events (Toggle)
@@ -107,8 +107,6 @@ dat += "No-one has done anything this round!" usr << browse(dat, "window=admin_log") - if("mentor_log") - CitadelMentorLogSecret() if("list_job_debug") var/dat = "Job Debug info.
" for(var/line in SSjob.job_debug) @@ -167,6 +165,23 @@ log_admin("[key_name(usr)] renamed the station to \"[new_name]\".") message_admins("[key_name_admin(usr)] renamed the station to: [new_name].") priority_announce("[command_name()] has renamed the station to \"[new_name]\".") + if("night_shift_set") + if(!check_rights(R_ADMIN)) + return + var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "On", "Off", "Automatic") + switch(val) + if("Automatic") + if(CONFIG_GET(flag/enable_night_shifts)) + SSnightshift.can_fire = TRUE + SSnightshift.fire() + else + SSnightshift.update_nightshift(FALSE, TRUE) + if("On") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(TRUE, TRUE) + if("Off") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(FALSE, TRUE) if("reset_name") if(!check_rights(R_ADMIN)) @@ -460,13 +475,6 @@ message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, 'sound/ai/commandreport.ogg') - if("ak47s") - if(!check_rights(R_FUN)) - return - message_admins("[key_name_admin(usr)] activated AK-47s for Everyone!") - usr.client.ak47s() - sound_to_playing_players('sound/misc/ak47s.ogg') - if("guns") if(!check_rights(R_FUN)) return @@ -613,13 +621,13 @@ var/list/new_movement = list() for(var/i in 1 to movement_keys.len) var/key = movement_keys[i] - + var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast" var/title = "New direction for [key]" var/new_direction = text2dir(input(usr, msg, title) as text|null) if(!new_direction) new_direction = movement_keys[key] - + new_movement[key] = new_direction SSinput.movement_keys = new_movement message_admins("[key_name_admin(usr)] has configured all movement directions.") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 91ed0a5128..3dc9b8fcd2 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -271,50 +271,6 @@ return create_message("note", banckey, null, banreason, null, null, 0, 0) - else if(href_list["mentor"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locate(href_list["mentor"]) - if(!ismob(M)) - to_chat(usr, "this can be only used on instances of type /mob!") - return - - if(!M.client) - to_chat(usr, "No client.") - return - - log_admin("[key_name(usr)] has granted [key_name(M)] mentor access") - message_admins(" [key_name_admin(usr)] has granted [key_name_admin(M)] mentor access.") - - var/datum/DBQuery/query_add_mentors = SSdbcore.NewQuery("INSERT INTO [format_table_name("mentor")] (ckey) VALUES ('[M.client.ckey]')") - if(!query_add_mentors.Execute()) - var/err = query_add_mentors.ErrorMsg() - log_game("SQL ERROR during adding new mentor. Error : \[[err]\]\n") - load_mentors() - M.verbs += /client/proc/cmd_mentor_say - M.verbs += /client/proc/show_mentor_memo - to_chat(M, " You've been granted mentor access! Help people who send mentor-pms.") - - else if(href_list["removementor"]) - if(!check_rights(R_ADMIN)) return - - var/mob/living/carbon/human/M = locate(href_list["removementor"]) - if(!ismob(M)) - usr << "this can be only used on instances of type /mob" - return - - log_admin("[key_name(usr)] has removed mentor access from [key_name(M)]") - message_admins(" [key_name_admin(usr)] has removed mentor access from [key_name_admin(M)].") - - var/datum/DBQuery/query_remove_mentors = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[M.client.ckey]'") - if(!query_remove_mentors.Execute()) - var/err = query_remove_mentors.ErrorMsg() - log_game("SQL ERROR during removing mentor. Error : \[[err]\]\n") - load_mentors() - to_chat(M, "Your mentor access has been revoked.") - M.verbs -= /client/proc/cmd_mentor_say - M.verbs -= /client/proc/show_mentor_memo - else if(href_list["editrights"]) edit_rights_topic(href_list) @@ -916,12 +872,6 @@ else dat += "Abductor" - //Borer - if(jobban_isbanned(M, "borer") || isbanned_dept) - dat += "Borer" - else - dat += "Borer" - //Alien if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept) dat += "Alien" @@ -1709,7 +1659,7 @@ var/mob/living/L = M var/status switch (M.stat) - if (CONSCIOUS) + if(CONSCIOUS) status = "Alive" if(SOFT_CRIT) status = "Dying" diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 33fb3867d1..7d382af901 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -24,7 +24,7 @@ admin_sound.status = SOUND_STREAM admin_sound.volume = vol - var/res = alert(usr, "Show the title of this song to the players?",, "No", "Yes", "Cancel") + var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel") switch(res) if("Yes") to_chat(world, "An admin played: [S]") diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 1d694e4033..36ecef0925 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -4,6 +4,7 @@ //Admin-spawn or random event #define INVISIBILITY_REVENANT 50 +#define REVENANT_NAME_FILE "revenant_names.json" /mob/living/simple_animal/revenant name = "\a Revenant" @@ -70,6 +71,15 @@ AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction(null)) + random_revenant_name() + +/mob/living/simple_animal/revenant/proc/random_revenant_name() + var/built_name = "" + built_name += pick(strings(REVENANT_NAME_FILE, "spirit_type")) + built_name += " of " + built_name += pick(strings(REVENANT_NAME_FILE, "adverb")) + built_name += pick(strings(REVENANT_NAME_FILE, "theme")) + name = built_name /mob/living/simple_animal/revenant/Login() ..() diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm index 5665ed5f69..138a7a2607 100644 --- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm +++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm @@ -98,11 +98,11 @@ var/list/new_overlay_types = tile_graphic() var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance - /*#if DM_VERSION >= 513 + #if DM_VERSION >= 513 #warning 512 is stable now for sure, remove the old code - #endif*/ + #endif - /*#if DM_VERSION >= 512 + #if DM_VERSION >= 512 if (atmos_overlay_types) for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added vars["vis_contents"] -= overlay @@ -112,7 +112,7 @@ vars["vis_contents"] += new_overlay_types - atmos_overlay_types //don't add overlays that already exist else vars["vis_contents"] += new_overlay_types - #else*/ + #else if (atmos_overlay_types) for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added cut_overlay(overlay) @@ -122,7 +122,7 @@ add_overlay(new_overlay_types - atmos_overlay_types) //don't add overlays that already exist else add_overlay(new_overlay_types) - //#endif + #endif UNSETEMPTY(new_overlay_types) src.atmos_overlay_types = new_overlay_types diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm index 1a9c76cb4d..79578b290d 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm @@ -57,11 +57,6 @@ It's like a regular ol' straight pipe, but you can turn it on and off. close() return open() - var/turf/T = get_turf(src) - var/area/A = get_area(src) - investigate_log("Valve, [src.name], was manipiulated by [key_name(usr)] at [x], [y], [z], [A]", "atmos") - message_admins("Valve, [src.name], was manipulated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(T)], [A]") - /obj/machinery/atmospherics/components/binary/valve/digital // can be controlled by AI name = "digital valve" diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index 6c4d691d0f..e5d07fd36b 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -57,7 +57,7 @@ continue // i'd be right happy to meme_pack_data[P.group]["packs"] += list(list( "name" = P.name, - "cost" = P.cost * 2, //displays twice the normal cost + "cost" = P.cost, "id" = pack )) @@ -120,12 +120,12 @@ CHECK_TICK if(empty_turfs && empty_turfs.len) var/LZ = empty_turfs[rand(empty_turfs.len-1)] - SSshuttle.points -= SO.pack.cost * 2 + SSshuttle.points -= SO.pack.cost new /obj/effect/DPtarget(LZ, SO, podID) . = TRUE update_icon() else - if(SO.pack.cost * (1.2*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^) + if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^) landingzone = locate(pick(GLOB.the_station_areas)) in GLOB.sortedAreas for(var/turf/open/floor/T in landingzone.contents) if(is_blocked_turf(T)) diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 41dade9fe0..f0c8e0c2a5 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -988,8 +988,9 @@ /obj/item/reagent_containers/pill/insulin, /obj/item/stack/medical/gauze, /obj/item/storage/box/beakers, + /obj/item/storage/box/medsprays, /obj/item/storage/box/syringes, - /obj/item/storage/box/bodybags) + /obj/item/storage/box/bodybags) crate_name = "medical supplies crate" /datum/supply_pack/medical/vending @@ -1983,4 +1984,4 @@ /obj/item/toy/redbutton, /obj/item/toy/eightball, /obj/item/vending_refill/donksoft) - crate_name = "toy crate" \ No newline at end of file + crate_name = "toy crate" diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index cccdb199bb..c821204993 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -97,7 +97,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the if(!verify) // Can't access the asset cache browser, rip. client.cache += unreceived return 1 - + client.sending |= unreceived var/job = ++client.last_asset_job @@ -135,7 +135,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the else concurrent_tracker++ send_asset(client, file, verify=FALSE) - + stoplag(0) //queuing calls like this too quickly can cause issues in some client versions //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. @@ -350,6 +350,11 @@ GLOBAL_LIST_EMPTY(asset_datums) "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', ) +/datum/asset/simple/permissions + assets = list( + "padlock.png" = 'html/padlock.png' + ) + //this exists purely to avoid meta by pre-loading all language icons. /datum/asset/language/register() for(var/path in typesof(/datum/language)) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9181945ebd..aa7f5ce82b 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -88,22 +88,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( cmd_admin_pm(href_list["priv_msg"],null) return - // Mentor PM - if(href_list["mentor_msg"]) - if(CONFIG_GET(flag.mentors_mobname_only)) - var/mob/M = locate(href_list["mentor_msg"]) - cmd_mentor_pm(M,null) - else - cmd_mentor_pm(href_list["mentor_msg"],null) - return - switch(href_list["_src_"]) if("holder") hsrc = holder if("usr") hsrc = mob - if("mentor") // CITADEL - hsrc = mentor_datum // CITADEL END if("prefs") if (inprefs) return @@ -239,16 +228,23 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) . = ..() //calls mob.Login() #if DM_VERSION >= 512 - if (num2text(byond_build) in GLOB.blacklisted_builds) - log_access("Failed login: blacklisted byond version") - to_chat(src, "Your version of byond is blacklisted.") - to_chat(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].") - to_chat(src, "Please download a new version of byond. if [byond_build] is the latest, you can go to http://www.byond.com/download/build/ to download other versions.") - if(connecting_admin) - to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions") - else + if (byond_version >= 512) + if (!byond_build || byond_build < 1386) + message_admins("[key_name(src)] has been detected as spoofing their byond version. Connection rejected.") + add_system_note("Spoofed-Byond-Version", "Detected as using a spoofed byond version.") + log_access("Failed Login: [key] - Spoofed byond version") qdel(src) - return + + if (num2text(byond_build) in GLOB.blacklisted_builds) + log_access("Failed login: [key] - blacklisted byond version") + to_chat(src, "Your version of byond is blacklisted.") + to_chat(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].") + to_chat(src, "Please download a new version of byond. if [byond_build] is the latest, you can go to http://www.byond.com/download/build/ to download other versions.") + if(connecting_admin) + to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions") + else + qdel(src) + return #endif if(SSinput.initialized) set_macros() @@ -380,8 +376,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if (menuitem) menuitem.Load_checked(src) - hook_vr("client_new",list(src)) // CIT CHANGE - hook for client/New() changes - Master.UpdateTickRate() ////////////// @@ -409,21 +403,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) "Someone come hold me :(",\ "I need someone on me :(",\ "What happened? Where has everyone gone?",\ - "Forever alone :(",\ - "My nipples are so stiff, but Zelda ain't here. :(",\ - "Leon senpai, play more Spessmans. :(",\ - "If only Serdy were here...",\ - "Panic bunker can't keep my love for you out.",\ - "Cebu needs to Awoo herself back into my heart.",\ - "I don't even have a Turry to snuggle viciously here.",\ - "MOM, WHERE ARE YOU??? D:",\ - "It's a beautiful day outside. Birds are singing, flowers are blooming. On days like this...kids like you...SHOULD BE BURNING IN HELL.",\ - "Sometimes when I have sex, I think about putting an entire peanut butter and jelly sandwich in the VCR.",\ - "Oh good, no-one around to watch me lick Goofball's nipples. :D",\ - "I've replaced Beepsky with a fidget spinner, glory be autism abuse.",\ - "i shure hop dere are no PRED arund!!!!",\ - "NO PRED CAN eVER CATCH MI",\ - "help, the clown is honking his horn in front of dorms and its interrupting everyones erp"\ + "Forever alone :("\ ) send2irc("Server", "[cheesy_message] (No admins online)") @@ -623,10 +603,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) to_chat(src, {"You will be automatically taken to the game, if not, click here to be taken manually"}) /client/proc/note_randomizer_user() - var/const/adminckey = "CID-Error" + add_system_note("CID-Error", "Detected as using a cid randomizer.") + +/client/proc/add_system_note(system_ckey, message) + var/sql_system_ckey = sanitizeSQL(system_ckey) var/sql_ckey = sanitizeSQL(ckey) //check to see if we noted them in the last day. - var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0") + var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[sql_system_ckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0") if(!query_get_notes.Execute()) return if(query_get_notes.NextRow()) @@ -636,9 +619,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if(!query_get_notes.Execute()) return if(query_get_notes.NextRow()) - if (query_get_notes.item[1] == adminckey) + if (query_get_notes.item[1] == system_ckey) return - create_message("note", sql_ckey, adminckey, "Detected as using a cid randomizer.", null, null, 0, 0) + create_message("note", ckey, system_ckey, message, null, null, 0, 0) /client/proc/check_ip_intel() @@ -730,12 +713,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if (isnull(new_size)) CRASH("change_view called without argument.") -//CIT CHANGES START HERE - makes change_view change DEFAULT_VIEW to 15x15 depending on preferences - if(prefs && CONFIG_GET(string/default_view)) - if(!prefs.widescreenpref && new_size == CONFIG_GET(string/default_view)) - new_size = "15x15" -//END OF CIT CHANGES - view = new_size apply_clickcatcher() if (isliving(mob)) @@ -754,4 +731,4 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) /client/proc/AnnouncePR(announcement) if(prefs && prefs.chat_toggles & CHAT_PULLR) - to_chat(src, announcement) \ No newline at end of file + to_chat(src, announcement) diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 52139fd7bc..255423a4fc 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -250,20 +250,6 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_ooc)() /datum/verbs/menu/Settings/listen_ooc/Get_checked(client/C) return C.prefs.chat_toggles & CHAT_OOC -TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglehoundsleeper)() - set name = "Allow/Deny Hound Sleeper" - set category = "Preferences" - set desc = "Allow MediHound Sleepers" - usr.client.prefs.toggles ^= MEDIHOUND_SLEEPER - usr.client.prefs.save_preferences() - if(usr.client.prefs.toggles & MEDIHOUND_SLEEPER) - to_chat(usr, "You will now allow MediHounds to place you in their sleeper.") - else - to_chat(usr, "You will no longer allow MediHounds to place you in their sleeper.") - SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle MediHound Sleeper", "[usr.client.prefs.toggles & MEDIHOUND_SLEEPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/datum/verbs/menu/Settings/Sound/togglehoundsleeper/Get_checked(client/C) - return C.prefs.toggles & MEDIHOUND_SLEEPER - GLOBAL_LIST_INIT(ghost_forms, list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \ "ghost_blue","ghost_yellow","ghost_green","ghost_pink", \ @@ -429,4 +415,3 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS prefs.save_preferences() to_chat(src, "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Prayer Visibility", "[prefs.chat_toggles & CHAT_PRAYER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index bb928c6fa5..3c49b113e9 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -20,6 +20,7 @@ var/blood_type = null var/list/features = null var/factions = null + var/list/traits = null var/contains_sample = 0 /obj/item/seeds/replicapod/attackby(obj/item/W, mob/user, params) @@ -34,6 +35,7 @@ blood_type = bloodSample.data["blood_type"] features = bloodSample.data["features"] factions = bloodSample.data["factions"] + traits = bloodSample.data["traits"] W.reagents.clear_reagents() to_chat(user, "You inject the contents of the syringe into the seeds.") contains_sample = 1 @@ -99,6 +101,8 @@ podman.faction |= factions if(!features["mcolor"]) features["mcolor"] = "#59CE00" + for(var/V in traits) + new V(podman) podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman podman.set_cloned_appearance() diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 054d97c9f1..1fd5e30424 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet) . = ..() var/turf/T = get_turf(src) GLOB.z_is_planet["[T.z]"] = TRUE + diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index 3e241fbc72..e73349198b 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -125,4 +125,4 @@ righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' force = 5 throwforce = 7 - w_class = WEIGHT_CLASS_SMALL \ No newline at end of file + w_class = WEIGHT_CLASS_SMALL diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 25eb19b644..358867e6ad 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -379,9 +379,8 @@ if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5) SSticker.mode.make_antag_chance(humanc) - for(var/V in character.roundstart_traits) - var/datum/trait/T = V - T.on_spawn() //so latejoins still get their correct traits + if(CONFIG_GET(flag/roundstart_traits)) + SStraits.AssignTraits(humanc, humanc.client, TRUE) log_manifest(character.mind.key,character.mind,character,latejoin = TRUE) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index f7b3d15155..41715d650d 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -191,6 +191,10 @@ blood_data["real_name"] = real_name blood_data["features"] = dna.features blood_data["factions"] = faction + blood_data["traits"] = list() + for(var/V in roundstart_traits) + var/datum/trait/T = V + blood_data["traits"] += T.type return blood_data //get the id of the substance this mob use as blood. diff --git a/code/modules/mob/living/carbon/human/species_types/corporate.dm b/code/modules/mob/living/carbon/human/species_types/corporate.dm index 552e413f4d..aa310723dd 100644 --- a/code/modules/mob/living/carbon/human/species_types/corporate.dm +++ b/code/modules/mob/living/carbon/human/species_types/corporate.dm @@ -17,4 +17,4 @@ use_skintones = 0 species_traits = list(SPECIES_ORGANIC,NOBLOOD,EYECOLOR) inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER) - sexes = 0 + sexes = 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 2297fffa97..41af8cc865 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -4,15 +4,12 @@ id = "jelly" default_color = "00FF90" say_mod = "chirps" - species_traits = list(SPECIES_ORGANIC,MUTCOLORS,EYECOLOR,,HAIR,FACEHAIR,NOBLOOD) + species_traits = list(SPECIES_ORGANIC,MUTCOLORS,EYECOLOR,NOBLOOD) inherent_traits = list(TRAIT_TOXINLOVER) - mutant_bodyparts = list("mam_tail", "mam_ears", "taur") //CIT CHANGE - default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None") //CIT CHANGE meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime exotic_blood = "slimejelly" damage_overlay_type = "" var/datum/action/innate/regenerate_limbs/regenerate_limbs - var/datum/action/innate/slime_change/slime_change //CIT CHANGE liked_food = MEAT coldmod = 6 // = 3x cold damage heatmod = 0.5 // = 1/4x heat damage @@ -21,8 +18,6 @@ /datum/species/jelly/on_species_loss(mob/living/carbon/C) if(regenerate_limbs) regenerate_limbs.Remove(C) - if(slime_change) //CIT CHANGE - slime_change.Remove(C) //CIT CHANGE C.remove_language(/datum/language/slime) C.faction -= "slime" ..() @@ -34,8 +29,6 @@ if(ishuman(C)) regenerate_limbs = new regenerate_limbs.Grant(C) - slime_change = new //CIT CHANGE - slime_change.Grant(C) //CIT CHANGE C.faction |= "slime" /datum/species/jelly/spec_life(mob/living/carbon/human/H) @@ -384,6 +377,7 @@ around.", "...and move this one instead.") + ///////////////////////////////////LUMINESCENTS////////////////////////////////////////// //Luminescents are able to consume and use slime extracts, without them decaying. @@ -542,7 +536,6 @@ if(species.current_extract) species.extract_cooldown = world.time + 100 - var/cooldown = species.current_extract.activate(H, species, activation_type) species.extract_cooldown = world.time + cooldown @@ -555,8 +548,6 @@ ///////////////////////////////////STARGAZERS////////////////////////////////////////// //Stargazers are the telepathic branch of jellypeople, able to project psychic messages and to link minds with willing participants. -//Admin spawn only - /datum/species/jelly/stargazer name = "Stargazer" @@ -725,5 +716,4 @@ to_chat(H, "You connect [target]'s mind to your slime link!") else to_chat(H, "You can't seem to link [target]'s mind...") - to_chat(target, "The foreign presence leaves your mind.") - + to_chat(target, "The foreign presence leaves your mind.") \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index 7f0d8afe26..8735d6ceb6 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -54,7 +54,7 @@ /datum/species/moth/space_move(mob/living/carbon/human/H) . = ..() - if(H.loc && !isspaceturf(H.loc) && H.dna.features["moth_wings"] != "Burnt Off" || "None") + if(H.loc && !isspaceturf(H.loc) && H.dna.features["moth_wings"] != "Burnt Off") var/datum/gas_mixture/current = H.loc.return_air() if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible return TRUE diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 7f6206dafb..aa1f5aff65 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -66,4 +66,4 @@ temp = master.supplied[index] if (length(temp) > 0) laws.supplied[index] = temp - return + return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 131748042c..2b1ff82af7 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -159,8 +159,6 @@ toner = tonermax diag_hud_set_borgcell() - verbs += /mob/living/proc/lay_down //CITADEL EDIT borgs have rest verb now for snowflake reasons - //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO /mob/living/silicon/robot/Destroy() if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. @@ -212,8 +210,6 @@ if(!CONFIG_GET(flag/disable_secborg)) modulelist["Security"] = /obj/item/robot_module/security - modulelist += get_cit_modules() //Citadel change - adds Citadel's borg modules. - var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in modulelist if(!input_module || module.type != /obj/item/robot_module) return @@ -367,12 +363,8 @@ to_chat(user, "You start fixing yourself...") if(!W.use_tool(src, user, 50)) return - adjustBruteLoss(-10) - else - to_chat(user, "You start fixing [src]...") - if(!do_after(user, 30, target = src)) - return - adjustBruteLoss(-30) + + adjustBruteLoss(-30) updatehealth() add_fingerprint(user) visible_message("[user] has fixed some of the dents on [src].") @@ -382,16 +374,11 @@ user.changeNext_move(CLICK_CD_MELEE) var/obj/item/stack/cable_coil/coil = W if (getFireLoss() > 0 || getToxLoss() > 0) - if(src == user && coil.use(1)) + if(src == user) to_chat(user, "You start fixing yourself...") if(!do_after(user, 50, target = src)) return - adjustFireLoss(-10) - adjustToxLoss(-10) if (coil.use(1)) - to_chat(user, "You start fixing [src]...") - if(!do_after(user, 30, target = src)) - return adjustFireLoss(-30) adjustToxLoss(-30) updatehealth() @@ -600,36 +587,6 @@ /mob/living/silicon/robot/update_icons() cut_overlays() icon_state = module.cyborg_base_icon - - //Citadel changes start here - Allows modules to use different icon files, and allows modules to specify a pixel offset - icon = (module.cyborg_icon_override ? module.cyborg_icon_override : initial(icon)) - if(laser) - add_overlay("laser")//Is this even used??? - Yes borg/inventory.dm - if(disabler) - add_overlay("disabler")//ditto - - if(sleeper_g && module.sleeper_overlay) - add_overlay("[module.sleeper_overlay]_g") - if(sleeper_r && module.sleeper_overlay) - add_overlay("[module.sleeper_overlay]_r") - if(module.dogborg == TRUE) - if(resting) - cut_overlays() - icon_state = "[module.cyborg_base_icon]-rest" - else - icon_state = "[module.cyborg_base_icon]" - - if(stat == DEAD && module.has_snowflake_deadsprite) - icon_state = "[module.cyborg_base_icon]-wreck" - - if(module.cyborg_pixel_offset) - pixel_x = module.cyborg_pixel_offset - //End of citadel changes - - if(module.cyborg_base_icon == "robot") - icon = 'icons/mob/robots.dmi' - pixel_x = initial(pixel_x) - if(stat != DEAD && !(IsUnconscious() || IsStun() || IsKnockdown() || low_power_mode)) //Not dead, not stunned. if(!eye_lights) eye_lights = new() @@ -1023,7 +980,6 @@ designation = module.name if(hands) hands.icon_state = module.moduleselect_icon - hands.icon = (module.moduleselect_alternate_icon ? module.moduleselect_alternate_icon : initial(hands.icon)) //CITADEL CHANGE - allows module select icons to use a different icon file if(module.can_be_pushed) status_flags |= CANPUSH else diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index a3bc98f48a..646987b155 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -22,7 +22,7 @@ ventcrawler = VENTCRAWLER_ALWAYS var/datum/mind/origin var/egg_lain = 0 - //gold_core_spawnable = HOSTILE_SPAWN //are you sure about this?? + gold_core_spawnable = NO_SPAWN //are you sure about this?? // CITADEL CHANGE, Yes. /mob/living/simple_animal/hostile/headcrab/proc/Infect(mob/living/carbon/victim) var/obj/item/organ/body_egg/changeling_egg/egg = new(victim) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 480b49277c..106381bade 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -146,13 +146,13 @@ else status_traits[trait] |= list(source) -/mob/living/proc/add_trait_datum(trait) //separate proc due to the way these ones are handled +/mob/living/proc/add_trait_datum(trait, spawn_effects) //separate proc due to the way these ones are handled if(has_trait(trait)) return if(!SStraits || !SStraits.traits[trait]) return var/datum/trait/T = SStraits.traits[trait] - new T (src) + new T (src, spawn_effects) return TRUE /mob/living/proc/remove_trait(trait, list/sources, force) @@ -192,13 +192,14 @@ . = FALSE + if(sources && !islist(sources)) + sources = list(sources) if(LAZYLEN(sources)) for(var/S in sources) if(S in status_traits[trait]) return TRUE - else - if(LAZYLEN(status_traits[trait])) - return TRUE + else if(LAZYLEN(status_traits[trait])) + return TRUE /mob/living/proc/has_trait_datum(trait) return roundstart_traits[trait] diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index ce4e1f7ad2..3b06b4c71e 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -16,6 +16,8 @@ var/recharged = 0 var/recharge_delay = 5 var/mutable_appearance/beaker_overlay + var/working_state = "dispenser_working" + var/nopower_state = "dispenser_nopower" var/obj/item/reagent_containers/beaker = null var/list/dispensable_reagents = list( "hydrogen", @@ -60,6 +62,7 @@ cell = new cell_type recharge() dispensable_reagents = sortList(dispensable_reagents) + update_icon() /obj/machinery/chem_dispenser/Destroy() QDEL_NULL(beaker) @@ -67,13 +70,36 @@ return ..() /obj/machinery/chem_dispenser/process() - if(recharged < 0) recharge() recharged = recharge_delay else recharged -= 1 +/obj/machinery/chem_dispenser/proc/display_beaker() + ..() + var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") + b_o.pixel_y = -4 + b_o.pixel_x = -7 + return b_o + +obj/machinery/chem_dispenser/proc/work_animation() + if(working_state) + flick(working_state,src) + +/obj/machinery/chem_dispenser/power_change() + ..() + if(!powered() && nopower_state) + icon_state = nopower_state + else + icon_state = initial(icon_state) + +obj/machinery/chem_dispenser/update_icon() + cut_overlays() + if(beaker) + beaker_overlay = display_beaker() + add_overlay(beaker_overlay) + /obj/machinery/chem_dispenser/proc/recharge() if(stat & (BROKEN|NOPOWER)) return @@ -163,6 +189,7 @@ var/target = text2num(params["target"]) if(target in beaker.possible_transfer_amounts) amount = target + work_animation() . = TRUE if("dispense") var/reagent = params["reagent"] @@ -173,11 +200,13 @@ R.add_reagent(reagent, actual) cell.use((actual / 10) / powerefficiency) + work_animation() . = TRUE if("remove") var/amount = text2num(params["amount"]) if(beaker && amount in beaker.possible_transfer_amounts) beaker.reagents.remove_all(amount) + work_animation() . = TRUE if("eject") if(beaker) @@ -185,7 +214,7 @@ if(Adjacent(usr) && !issilicon(usr)) usr.put_in_hands(beaker) beaker = null - cut_overlays() + update_icon() . = TRUE if("dispense_recipe") var/recipe_to_use = params["recipe"] @@ -200,6 +229,7 @@ if(actual) R.add_reagent(r_id, actual) cell.use((actual / 10) / powerefficiency) + work_animation() if("clear_recipes") var/yesno = alert("Clear all recipes?",, "Yes","No") if(yesno == "Yes") @@ -226,23 +256,17 @@ /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) if(default_unfasten_wrench(user, I)) return - if(istype(I, /obj/item/reagent_containers) && !(I.flags_1 & ABSTRACT_1) && I.is_open_container()) var/obj/item/reagent_containers/B = I . = 1 //no afterattack if(beaker) to_chat(user, "A container is already loaded into [src]!") return - if(!user.transferItemToLoc(B, src)) return - beaker = B to_chat(user, "You add [B] to [src].") - - beaker_overlay = beaker_overlay || mutable_appearance(icon, "disp_beaker") - beaker_overlay.pixel_x = rand(-10, 5)//randomize beaker overlay position. - add_overlay(beaker_overlay) + update_icon() else if(user.a_intent != INTENT_HARM && !istype(I, /obj/item/card/emag)) to_chat(user, "You can't load [I] into [src]!") return ..() @@ -266,6 +290,7 @@ beaker.reagents.remove_all() cell.use(total/powerefficiency) cell.emp_act(severity) + work_animation() visible_message("[src] malfunctions, spraying chemicals everywhere!") ..() @@ -278,6 +303,8 @@ recharge_delay = 20 dispensable_reagents = list() circuit = /obj/item/circuitboard/machine/chem_dispenser + working_state = "minidispenser_working" + nopower_state = "minidispenser_nopower" var/static/list/dispensable_reagent_tiers = list( list( "hydrogen", @@ -362,6 +389,29 @@ final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2])) return final_list +/obj/machinery/chem_dispenser/constructable/display_beaker() + var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") + b_o.pixel_y = -4 + b_o.pixel_x = -4 + return b_o + +/obj/machinery/chem_dispenser/drinks/display_beaker() + var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") + switch(dir) + if(NORTH) + b_o.pixel_y = 7 + b_o.pixel_x = rand(-9, 9) + if(EAST) + b_o.pixel_x = 4 + b_o.pixel_y = rand(-5, 7) + if(WEST) + b_o.pixel_x = -5 + b_o.pixel_y = rand(-5, 7) + else//SOUTH + b_o.pixel_y = -7 + b_o.pixel_x = rand(-9, 9) + return b_o + /obj/machinery/chem_dispenser/drinks name = "soda dispenser" desc = "Contains a large reservoir of soft drinks." @@ -369,6 +419,10 @@ icon = 'icons/obj/chemical.dmi' icon_state = "soda_dispenser" amount = 10 + pixel_y = 6 + layer = WALL_OBJ_LAYER + working_state = null + nopower_state = null dispensable_reagents = list( "water", "ice", @@ -398,8 +452,6 @@ "tirizene" ) - - /obj/machinery/chem_dispenser/drinks/beer name = "booze dispenser" desc = "Contains a large reservoir of the good stuff." diff --git a/code/modules/reagents/chemistry/machinery/scp_294.dm b/code/modules/reagents/chemistry/machinery/scp_294.dm index 2b63799547..5aa09d407b 100644 --- a/code/modules/reagents/chemistry/machinery/scp_294.dm +++ b/code/modules/reagents/chemistry/machinery/scp_294.dm @@ -14,6 +14,8 @@ icon_state = "294_bottom" amount = 10 resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF + working_state = null + nopower_state = null var/static/list/shortcuts = list( "meth" = "methamphetamine", "tricord" = "tricordrazine" @@ -25,7 +27,7 @@ GLOB.poi_list += src top_overlay = mutable_appearance(icon, "294_top", layer = ABOVE_ALL_MOB_LAYER) update_icon() - + /obj/machinery/chem_dispenser/scp_294/update_icon() cut_overlays() @@ -36,6 +38,9 @@ GLOB.poi_list -= src QDEL_NULL(top_overlay) +/obj/machinery/chem_dispenser/scp_294/display_beaker() + return + /obj/machinery/chem_dispenser/scp_294/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm new file mode 100644 index 0000000000..2f715084ad --- /dev/null +++ b/code/modules/reagents/reagent_containers/medspray.dm @@ -0,0 +1,91 @@ +/obj/item/reagent_containers/medspray + name = "medical spray" + desc = "A medical spray bottle, designed for precision application, with an unscrewable cap." + icon = 'icons/obj/chemical.dmi' + icon_state = "medspray" + item_state = "spraycan" + lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' + flags_1 = NOBLUDGEON_1 + obj_flags = UNIQUE_RENAME + container_type = OPENCONTAINER + slot_flags = SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 7 + amount_per_transfer_from_this = 10 + volume = 60 + var/can_fill_from_container = TRUE + var/apply_type = PATCH + var/apply_method = "spray" + var/self_delay = 30 + var/squirt_mode = 0 + var/squirt_amount = 5 + +/obj/item/reagent_containers/medspray/attack_self(mob/user) + squirt_mode = !squirt_mode + if(squirt_mode) + amount_per_transfer_from_this = squirt_amount + else + amount_per_transfer_from_this = initial(amount_per_transfer_from_this) + to_chat(user, "You will now apply the medspray's contents in [squirt_mode ? "short bursts":"extended sprays"]. You'll now use [amount_per_transfer_from_this] units per use.") + +/obj/item/reagent_containers/medspray/attack(mob/M, mob/user, def_zone) + if(!reagents || !reagents.total_volume) + to_chat(user, "[src] is empty!") + return + + if(M == user) + M.visible_message("[user] attempts to [apply_method] [src] on themselves.") + if(self_delay) + if(!do_mob(user, M, self_delay)) + return + if(!reagents || !reagents.total_volume) + return + to_chat(M, "You [apply_method] yourself with [src].") + + else + add_logs(user, M, "attempted to apply", src, reagents.log_list()) + M.visible_message("[user] attempts to [apply_method] [src] on [M].", \ + "[user] attempts to [apply_method] [src] on [M].") + if(!do_mob(user, M)) + return + if(!reagents || !reagents.total_volume) + return + M.visible_message("[user] [apply_method]s [M] down with [src].", \ + "[user] [apply_method]s [M] down with [src].") + + if(!reagents || !reagents.total_volume) + return + + else + add_logs(user, M, "applied", src, reagents.log_list()) + playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6) + var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) + reagents.reaction(M, apply_type, fraction) + reagents.trans_to(M, amount_per_transfer_from_this) + return + +/obj/item/reagent_containers/medspray/styptic + name = "medical spray (styptic powder)" + desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains styptic powder, for treating cuts and bruises." + icon_state = "brutespray" + list_reagents = list("styptic_powder" = 60) + +/obj/item/reagent_containers/medspray/silver_sulf + name = "medical spray (silver sulfadiazine)" + desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains silver sulfadiazine, useful for treating burns." + icon_state = "burnspray" + list_reagents = list("silver_sulfadiazine" = 60) + +/obj/item/reagent_containers/medspray/synthflesh + name = "medical spray (synthflesh)" + desc = "A medical spray bottle, designed for precision application, with an unscrewable cap. This one contains synthflesh, an apex brute and burn healing agent." + icon_state = "synthspray" + list_reagents = list("synthflesh" = 60) + +/obj/item/reagent_containers/medspray/sterilizine + name = "sterilizer spray" + desc = "Spray bottle loaded with non-toxic sterilizer. Useful in preparation for surgery." + list_reagents = list("sterilizine" = 60) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 68507d673a..655f6ffc7d 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -171,19 +171,6 @@ list_reagents = list("spraytan" = 50) -/obj/item/reagent_containers/spray/medical - name = "medical spray" - icon = 'icons/obj/chemical.dmi' - icon_state = "medspray" - volume = 100 - - -/obj/item/reagent_containers/spray/medical/sterilizer - name = "sterilizer spray" - desc = "Spray bottle loaded with non-toxic sterilizer. Useful in preparation for surgery." - list_reagents = list("sterilizine" = 100) - - //pepperspray /obj/item/reagent_containers/spray/pepper name = "pepperspray" diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm index 61c3f8833f..282cfada7f 100644 --- a/code/modules/security_levels/security_levels.dm +++ b/code/modules/security_levels/security_levels.dm @@ -41,8 +41,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN) if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) SSshuttle.emergency.modTimer(2) GLOB.security_level = SEC_LEVEL_BLUE - sound_to_playing_players('sound/misc/voybluealert.ogg') // Citadel change - Makes alerts play a sound - for(var/obj/machinery/firealarm/FA in GLOB.machines) if(is_station_level(FA.z)) FA.update_icon() @@ -57,7 +55,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN) else minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code red!") GLOB.security_level = SEC_LEVEL_RED - sound_to_playing_players('sound/misc/voyalert.ogg') // Citadel change - Makes alerts play a sound for(var/obj/machinery/firealarm/FA in GLOB.machines) if(is_station_level(FA.z)) @@ -72,8 +69,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN) else if(GLOB.security_level == SEC_LEVEL_BLUE) SSshuttle.emergency.modTimer(0.5) GLOB.security_level = SEC_LEVEL_DELTA - sound_to_playing_players('sound/misc/deltakalaxon.ogg') // Citadel change - Makes alerts play a sound - for(var/obj/machinery/firealarm/FA in GLOB.machines) if(is_station_level(FA.z)) FA.update_icon() diff --git a/code/modules/spells/spell_types/emplosion.dm b/code/modules/spells/spell_types/emplosion.dm index e6393e8584..8c45c06379 100644 --- a/code/modules/spells/spell_types/emplosion.dm +++ b/code/modules/spells/spell_types/emplosion.dm @@ -15,4 +15,4 @@ continue empulse(target.loc, emp_heavy, emp_light) - return + return \ No newline at end of file diff --git a/code/modules/spells/spell_types/inflict_handler.dm b/code/modules/spells/spell_types/inflict_handler.dm index a1ba69b426..da0af7a601 100644 --- a/code/modules/spells/spell_types/inflict_handler.dm +++ b/code/modules/spells/spell_types/inflict_handler.dm @@ -49,4 +49,4 @@ target.blur_eyes(amt_eye_blurry) //summoning if(summon_type) - new summon_type(target.loc, target) + new summon_type(target.loc, target) \ No newline at end of file diff --git a/icons/mob/actions/actions_slime.dmi b/icons/mob/actions/actions_slime.dmi index 23fd6e3e8a..acf7a31c6e 100644 Binary files a/icons/mob/actions/actions_slime.dmi and b/icons/mob/actions/actions_slime.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index d180eb445c..8f27d8b3bb 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/screen_full.dmi b/icons/mob/screen_full.dmi index 76c3672627..502e9ad3f9 100644 Binary files a/icons/mob/screen_full.dmi and b/icons/mob/screen_full.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 241db46d08..a4b426ccd9 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index 367be13b3b..1aece35214 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index e97dc22159..5e6e6e54d2 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index a494c1081c..de5f448ddd 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index cd15db0552..b3774b26ab 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/hydroponics/harvest.dmi b/icons/obj/hydroponics/harvest.dmi index 742c02985d..054aa47bbd 100644 Binary files a/icons/obj/hydroponics/harvest.dmi and b/icons/obj/hydroponics/harvest.dmi differ diff --git a/modular_citadel/code/modules/client/preferences_toggles.dm b/modular_citadel/code/modules/client/preferences_toggles.dm index 4b92a1e9c3..a475d65106 100644 --- a/modular_citadel/code/modules/client/preferences_toggles.dm +++ b/modular_citadel/code/modules/client/preferences_toggles.dm @@ -20,3 +20,17 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggledigestionnoise)() to_chat(usr, "You will [(usr.client.prefs.toggles & DIGESTION_NOISES) ? "now" : "no longer"] hear digestion noises.") /datum/verbs/menu/Settings/Sound/toggledigestionnoise/Get_checked(client/C) return !(C.prefs.toggles & DIGESTION_NOISES) + +TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglehoundsleeper)() + set name = "Allow/Deny Hound Sleeper" + set category = "Preferences" + set desc = "Allow MediHound Sleepers" + usr.client.prefs.toggles ^= MEDIHOUND_SLEEPER + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & MEDIHOUND_SLEEPER) + to_chat(usr, "You will now allow MediHounds to place you in their sleeper.") + else + to_chat(usr, "You will no longer allow MediHounds to place you in their sleeper.") + SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle MediHound Sleeper", "[usr.client.prefs.toggles & MEDIHOUND_SLEEPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/verbs/menu/Settings/Sound/togglehoundsleeper/Get_checked(client/C) + return C.prefs.toggles & MEDIHOUND_SLEEPER \ No newline at end of file