Merge remote-tracking branch 'refs/remotes/origin/master' into sync

This commit is contained in:
Poojawa
2018-03-06 21:47:22 -06:00
71 changed files with 784 additions and 575 deletions

View File

@@ -394,4 +394,4 @@
min_val = 0
/datum/config_entry/string/default_view
config_entry_value = "15x15"
config_entry_value = "15x15"

View File

@@ -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)

View File

@@ -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)

View File

@@ -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")

View File

@@ -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 = {"<form><input type="hidden" name="src" value="[REF(src)]"><ul class="sparse">"}
if (inputtype == "checkbox" || inputtype == "radio")
for (var/i in values)
var/div_slider = slidecolor
if(!i["allowed_edit"])
div_slider = "locked"
output += {"<li>
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""]>
<div class="slider"></div>
<span>[i["name"]]</span>
</label>
</li>"}
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""][i["allowed_edit"] ? "" : " onclick='return false' onkeydown='return false'"]>
<div class="slider [div_slider ? "[div_slider]" : ""]"></div>
<span>[i["name"]]</span>
</label>
</li>"}
else
for (var/i in values)
output += {"<li><input id="name="[i["name"]]"" style="width: 50px" type="[type]" name="[i["name"]]" value="[i["value"]]">
<label for="[i["name"]]">[i["name"]]</label></li>"}
<label for="[i["name"]]">[i["name"]]</label></li>"}
output += {"</ul><div style="text-align:center">
<button type="submit" name="button" value="1" style="font-size:large;float:[( Button2 ? "left" : "right" )]">[Button1]</button>"}
@@ -252,7 +254,7 @@
output += {"<button type="submit" name="button" value="3" style="font-size:large;float:right">[Button3]</button>"}
output += {"</form></div>"}
..(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

View File

@@ -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))

View File

@@ -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("<br>")
/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

View File

@@ -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)

View File

@@ -542,4 +542,4 @@
if(EMERGENCY_ESCAPED_OR_ENDGAMED)
SSticker.news_report = STATION_EVACUATED
if(SSshuttle.emergency.is_hijacked())
SSticker.news_report = SHUTTLE_HIJACK
SSticker.news_report = SHUTTLE_HIJACK

View File

@@ -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, "<span class='notice'>You force an emergency ejection. </span>")
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)

View File

@@ -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"]] => <font class='good'>Cloning cycle in progress...</font>"
records -= R
@@ -409,7 +409,7 @@
else if(pod.occupant)
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
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"]] => <font class='good'>Cloning cycle in progress...</font>"
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)]"

View File

@@ -75,4 +75,4 @@
return 0
if(B.scrambledcodes || B.emagged)
return 0
return ..()
return ..()

View File

@@ -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

View File

@@ -68,4 +68,4 @@
/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user)
if(stat & NOPOWER)
open(1)
open(1)

View File

@@ -178,3 +178,4 @@
priority = "Extreme"
else
priority = "Undetermined"

View File

@@ -19,3 +19,4 @@
/obj/mecha/combat/durand/RemoveActions(mob/living/user, human_occupant = 0)
..()
defense_action.Remove(user)

View File

@@ -27,3 +27,4 @@
..()
switch_damtype_action.Remove(user)
phasing_action.Remove(user)

View File

@@ -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"

View File

@@ -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)

View File

@@ -648,4 +648,4 @@
item_state = "defibpaddles0"
req_defib = FALSE
#undef HALFWAYCRITDEATH
#undef HALFWAYCRITDEATH

View File

@@ -1,4 +1,3 @@
//CREATOR'S NOTE: DO NOT FUCKING GIVE THIS TO BOTANY!
/obj/item/hot_potato
name = "hot potato"

View File

@@ -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."

View File

@@ -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

View File

@@ -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"

View File

@@ -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)
new /obj/item/clothing/head/helmet/bluetaghelm(src)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>")
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, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
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, "<font color='red'>Error: Topic 'editrights': [new_ckey] is already an admin</font>")
return
adm_ckey = new_ckey
task = "rank"
else
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
to_chat(usr, "<font color='red'>Error: Topic 'editrights': No valid ckey</font>")
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()

View File

@@ -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]")

View File

@@ -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

View File

@@ -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({"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
<link rel='stylesheet' type='text/css' href='panels.css'>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;text-align:right;'>CKEY <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:40%;'>PERMISSIONS</th>
<th style='width:20%;'>DENIED</th>
<th style='width:40%;'>ALLOWED TO EDIT</th>
</tr>
"})
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 = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.include_rights," ")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.exclude_rights," ", "-")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.can_edit_rights," ", "*")]</a></td>"
output += "</tr>"
output += {"
</table></div>
<div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div>
</body>
</html>"}
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, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
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, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>")
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, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
return
if(check_rights(R_DBRANKS, 0))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
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, "<span class='danger'>[.] is already an admin.</span>")
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<br>[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<br>Flags enabled here will be removed from a rank.<br>Note these take precedence over included flags.<br>[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<br>These are the flags this rank is allowed to edit if they have access to the permissions panel.<br>They will be unable to modify admins to a rank that has a flag not included here.<br>[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"]")

View File

@@ -31,6 +31,7 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=tdomereset'>Reset Thunderdome to default state</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=set_name'>Rename Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=reset_name'>Reset Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=night_shift_set'>Set Night Shift Mode</A><BR>
<BR>
<B>Shuttles</B><BR>
<BR>
@@ -54,7 +55,6 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=quickpower'>Power all SMES</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=tripleAI'>Triple AI mode (needs to be used in the lobby)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=traitor_all'>Everyone is the traitor</A><BR>
<A href='?src=\ref[src];[HrefToken()];secrets=ak47s'>AK-47s For Everyone!</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=guns'>Summon Guns</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=magic'>Summon Magic</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=events'>Summon Events (Toggle)</A><BR>
@@ -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 = "<B>Job Debug info.</B><HR>"
for(var/line in SSjob.job_debug)
@@ -167,6 +165,23 @@
log_admin("[key_name(usr)] renamed the station to \"[new_name]\".")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] renamed the station to: [new_name].</span>")
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.")

View File

@@ -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, "<span class='danger'>this can be only used on instances of type /mob!</span>")
return
if(!M.client)
to_chat(usr, "<span class='danger'>No client.</span>")
return
log_admin("[key_name(usr)] has granted [key_name(M)] mentor access")
message_admins("<span class='adminnotice'> [key_name_admin(usr)] has granted [key_name_admin(M)] mentor access.</span>")
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, "<span class='adminnotice'> You've been granted mentor access! Help people who send mentor-pms.</span>")
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("<span class='adminnotice'> [key_name_admin(usr)] has removed mentor access from [key_name_admin(M)].</span>")
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, "<span class='adminnotice'>Your mentor access has been revoked.</span>")
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 += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=abductor;jobban4=[REF(M)]'>Abductor</a></td>"
//Borer
if(jobban_isbanned(M, "borer") || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=borer;jobban4=[REF(M)]'><font color=red>Borer</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=borer;jobban4=[REF(M)]'>Borer</a></td>"
//Alien
if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=alien;jobban4=[REF(M)]'><font color=red>Alien</font></a></td>"
@@ -1709,7 +1659,7 @@
var/mob/living/L = M
var/status
switch (M.stat)
if (CONSCIOUS)
if(CONSCIOUS)
status = "Alive"
if(SOFT_CRIT)
status = "<font color='orange'><b>Dying</b></font>"

View File

@@ -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, "<span class='boldannounce'>An admin played: [S]</span>")

View File

@@ -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()
..()

View File

@@ -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

View File

@@ -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"

View File

@@ -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))

View File

@@ -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"
crate_name = "toy crate"

View File

@@ -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))

View File

@@ -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, "<span class='userdanger'>Your version of byond is blacklisted.</span>")
to_chat(src, "<span class='danger'>Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].</span>")
to_chat(src, "<span class='danger'>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.</span>")
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("<span class='adminnotice'>[key_name(src)] has been detected as spoofing their byond version. Connection rejected.</span>")
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, "<span class='userdanger'>Your version of byond is blacklisted.</span>")
to_chat(src, "<span class='danger'>Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].</span>")
to_chat(src, "<span class='danger'>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.</span>")
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, {"<a href="byond://[url]?token=[token]">You will be automatically taken to the game, if not, click here to be taken manually</a>"})
/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)
to_chat(src, announcement)

View File

@@ -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!

View File

@@ -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, "<span class='notice'>You inject the contents of the syringe into the seeds.</span>")
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()

View File

@@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet)
. = ..()
var/turf/T = get_turf(src)
GLOB.z_is_planet["[T.z]"] = TRUE

View File

@@ -125,4 +125,4 @@
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
force = 5
throwforce = 7
w_class = WEIGHT_CLASS_SMALL
w_class = WEIGHT_CLASS_SMALL

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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.</span>",
"<span class='notice'>...and move this one instead.</span>")
///////////////////////////////////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, "<span class='notice'>You connect [target]'s mind to your slime link!</span>")
else
to_chat(H, "<span class='warning'>You can't seem to link [target]'s mind...</span>")
to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>")
to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>")

View File

@@ -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

View File

@@ -66,4 +66,4 @@
temp = master.supplied[index]
if (length(temp) > 0)
laws.supplied[index] = temp
return
return

View File

@@ -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, "<span class='notice'>You start fixing yourself...</span>")
if(!W.use_tool(src, user, 50))
return
adjustBruteLoss(-10)
else
to_chat(user, "<span class='notice'>You start fixing [src]...</span>")
if(!do_after(user, 30, target = src))
return
adjustBruteLoss(-30)
adjustBruteLoss(-30)
updatehealth()
add_fingerprint(user)
visible_message("<span class='notice'>[user] has fixed some of the dents on [src].</span>")
@@ -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, "<span class='notice'>You start fixing yourself...</span>")
if(!do_after(user, 50, target = src))
return
adjustFireLoss(-10)
adjustToxLoss(-10)
if (coil.use(1))
to_chat(user, "<span class='notice'>You start fixing [src]...</span>")
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

View File

@@ -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)

View File

@@ -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]

View File

@@ -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, "<span class='warning'>A container is already loaded into [src]!</span>")
return
if(!user.transferItemToLoc(B, src))
return
beaker = B
to_chat(user, "<span class='notice'>You add [B] to [src].</span>")
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, "<span class='warning'>You can't load [I] into [src]!</span>")
return ..()
@@ -266,6 +290,7 @@
beaker.reagents.remove_all()
cell.use(total/powerefficiency)
cell.emp_act(severity)
work_animation()
visible_message("<span class='danger'>[src] malfunctions, spraying chemicals everywhere!</span>")
..()
@@ -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."

View File

@@ -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)

View File

@@ -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, "<span class='notice'>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.</span>")
/obj/item/reagent_containers/medspray/attack(mob/M, mob/user, def_zone)
if(!reagents || !reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(M == user)
M.visible_message("<span class='notice'>[user] attempts to [apply_method] [src] on themselves.</span>")
if(self_delay)
if(!do_mob(user, M, self_delay))
return
if(!reagents || !reagents.total_volume)
return
to_chat(M, "<span class='notice'>You [apply_method] yourself with [src].</span>")
else
add_logs(user, M, "attempted to apply", src, reagents.log_list())
M.visible_message("<span class='danger'>[user] attempts to [apply_method] [src] on [M].</span>", \
"<span class='userdanger'>[user] attempts to [apply_method] [src] on [M].</span>")
if(!do_mob(user, M))
return
if(!reagents || !reagents.total_volume)
return
M.visible_message("<span class='danger'>[user] [apply_method]s [M] down with [src].</span>", \
"<span class='userdanger'>[user] [apply_method]s [M] down with [src].</span>")
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)

View File

@@ -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"

View File

@@ -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()

View File

@@ -15,4 +15,4 @@
continue
empulse(target.loc, emp_heavy, emp_light)
return
return

View File

@@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 KiB

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -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