Merge remote-tracking branch 'refs/remotes/origin/master' into sync
@@ -394,4 +394,4 @@
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/default_view
|
||||
config_entry_value = "15x15"
|
||||
config_entry_value = "15x15"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)]"
|
||||
|
||||
@@ -75,4 +75,4 @@
|
||||
return 0
|
||||
if(B.scrambledcodes || B.emagged)
|
||||
return 0
|
||||
return ..()
|
||||
return ..()
|
||||
@@ -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
|
||||
|
||||
@@ -68,4 +68,4 @@
|
||||
|
||||
/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user)
|
||||
if(stat & NOPOWER)
|
||||
open(1)
|
||||
open(1)
|
||||
|
||||
@@ -178,3 +178,4 @@
|
||||
priority = "Extreme"
|
||||
else
|
||||
priority = "Undetermined"
|
||||
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
/obj/mecha/combat/durand/RemoveActions(mob/living/user, human_occupant = 0)
|
||||
..()
|
||||
defense_action.Remove(user)
|
||||
|
||||
|
||||
@@ -27,3 +27,4 @@
|
||||
..()
|
||||
switch_damtype_action.Remove(user)
|
||||
phasing_action.Remove(user)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -648,4 +648,4 @@
|
||||
item_state = "defibpaddles0"
|
||||
req_defib = FALSE
|
||||
|
||||
#undef HALFWAYCRITDEATH
|
||||
#undef HALFWAYCRITDEATH
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
//CREATOR'S NOTE: DO NOT FUCKING GIVE THIS TO BOTANY!
|
||||
/obj/item/hot_potato
|
||||
name = "hot potato"
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
274
code/modules/admin/permissionedit.dm
Normal 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"]")
|
||||
@@ -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.")
|
||||
|
||||
@@ -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>"
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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()
|
||||
..()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet)
|
||||
. = ..()
|
||||
var/turf/T = get_turf(src)
|
||||
GLOB.z_is_planet["[T.z]"] = TRUE
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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>")
|
||||
@@ -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
|
||||
|
||||
@@ -66,4 +66,4 @@
|
||||
temp = master.supplied[index]
|
||||
if (length(temp) > 0)
|
||||
laws.supplied[index] = temp
|
||||
return
|
||||
return
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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)
|
||||
|
||||
91
code/modules/reagents/reagent_containers/medspray.dm
Normal 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)
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
continue
|
||||
empulse(target.loc, emp_heavy, emp_light)
|
||||
|
||||
return
|
||||
return
|
||||
@@ -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)
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 4.1 MiB After Width: | Height: | Size: 4.1 MiB |
|
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 49 KiB |
@@ -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
|
||||