Merge pull request #5840 from Poojawa/reverts-crybaby-inducer
Reverts crybaby inducer also does the sync things
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
#define R_SOUNDS 0x800
|
||||
#define R_SPAWN 0x1000
|
||||
#define R_AUTOLOGIN 0x2000
|
||||
#define R_DBRANKS 0x4000
|
||||
|
||||
#define R_DEFAULT R_AUTOLOGIN
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow.
|
||||
|
||||
#define LIGHTING_PLANE_ALPHA_VISIBLE 255
|
||||
#define LIGHTING_PLANE_ALPHA_NV_TRAIT 250
|
||||
#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245
|
||||
#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192
|
||||
#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 //For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell.
|
||||
#define LIGHTING_PLANE_ALPHA_INVISIBLE 0
|
||||
|
||||
@@ -22,3 +22,4 @@
|
||||
#define QDELING(X) (X.gc_destroyed)
|
||||
#define QDELETED(X) (!X || QDELING(X))
|
||||
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//Update this whenever the db schema changes
|
||||
//make sure you add an update to the schema_version stable in the db changelog
|
||||
#define DB_MAJOR_VERSION 4
|
||||
#define DB_MINOR_VERSION 0
|
||||
#define DB_MINOR_VERSION 1
|
||||
|
||||
//Timing subsystem
|
||||
//Don't run if there is an identical unique timer active
|
||||
|
||||
@@ -500,3 +500,28 @@
|
||||
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
count++
|
||||
return objective_parts.Join("<br>")
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/save_admin_data()
|
||||
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
|
||||
return
|
||||
else if(load_admins()) //returns true if there was a database failure and the backup was loaded from
|
||||
return
|
||||
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
|
||||
query_admin_rank_update.Execute()
|
||||
//json format backup file generation stored per server
|
||||
var/json_file = file("data/admins_backup.json")
|
||||
var/list/file_data = list("ranks" = list(), "admins" = list())
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
file_data["ranks"]["[R.name]"] = list()
|
||||
file_data["ranks"]["[R.name]"]["include rights"] = R.include_rights
|
||||
file_data["ranks"]["[R.name]"]["exclude rights"] = R.exclude_rights
|
||||
file_data["ranks"]["[R.name]"]["can edit rights"] = R.can_edit_rights
|
||||
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
|
||||
file_data["admins"]["[i]"] = A.rank.name
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
+21
-19
@@ -182,38 +182,40 @@
|
||||
return ICON_OVERLAY
|
||||
|
||||
//Converts a rights bitfield into a string
|
||||
/proc/rights2text(rights, seperator="", list/adds, list/subs)
|
||||
/proc/rights2text(rights, seperator="", prefix = "+")
|
||||
seperator += prefix
|
||||
if(rights & R_BUILDMODE)
|
||||
. += "[seperator]+BUILDMODE"
|
||||
. += "[seperator]BUILDMODE"
|
||||
if(rights & R_ADMIN)
|
||||
. += "[seperator]+ADMIN"
|
||||
. += "[seperator]ADMIN"
|
||||
if(rights & R_BAN)
|
||||
. += "[seperator]+BAN"
|
||||
. += "[seperator]BAN"
|
||||
if(rights & R_FUN)
|
||||
. += "[seperator]+FUN"
|
||||
. += "[seperator]FUN"
|
||||
if(rights & R_SERVER)
|
||||
. += "[seperator]+SERVER"
|
||||
. += "[seperator]SERVER"
|
||||
if(rights & R_DEBUG)
|
||||
. += "[seperator]+DEBUG"
|
||||
. += "[seperator]DEBUG"
|
||||
if(rights & R_POSSESS)
|
||||
. += "[seperator]+POSSESS"
|
||||
. += "[seperator]POSSESS"
|
||||
if(rights & R_PERMISSIONS)
|
||||
. += "[seperator]+PERMISSIONS"
|
||||
. += "[seperator]PERMISSIONS"
|
||||
if(rights & R_STEALTH)
|
||||
. += "[seperator]+STEALTH"
|
||||
. += "[seperator]STEALTH"
|
||||
if(rights & R_POLL)
|
||||
. += "[seperator]+POLL"
|
||||
. += "[seperator]POLL"
|
||||
if(rights & R_VAREDIT)
|
||||
. += "[seperator]+VAREDIT"
|
||||
. += "[seperator]VAREDIT"
|
||||
if(rights & R_SOUNDS)
|
||||
. += "[seperator]+SOUND"
|
||||
. += "[seperator]SOUND"
|
||||
if(rights & R_SPAWN)
|
||||
. += "[seperator]+SPAWN"
|
||||
|
||||
for(var/verbpath in adds)
|
||||
. += "[seperator]+[verbpath]"
|
||||
for(var/verbpath in subs)
|
||||
. += "[seperator]-[verbpath]"
|
||||
. += "[seperator]SPAWN"
|
||||
if(rights & R_AUTOLOGIN)
|
||||
. += "[seperator]AUTOLOGIN"
|
||||
if(rights & R_DBRANKS)
|
||||
. += "[seperator]DBRANKS"
|
||||
if(!.)
|
||||
. = "NONE"
|
||||
return .
|
||||
|
||||
/proc/ui_style2icon(ui_style)
|
||||
|
||||
@@ -22,5 +22,6 @@ GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
|
||||
GLOBAL_LIST_INIT(bitfields, list(
|
||||
"obj_flags" = list("EMAGGED" = EMAGGED, "IN_USE" = IN_USE, "CAN_BE_HIT" = CAN_BE_HIT, "BEING_SHOCKED" = BEING_SHOCKED, "DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION, "ON_BLUEPRINTS" = ON_BLUEPRINTS, "UNIQUE_RENAME" = UNIQUE_RENAME),
|
||||
"datum_flags" = list("DF_USE_TAG" = DF_USE_TAG, "DF_VAR_EDITED" = DF_VAR_EDITED),
|
||||
"item_flags" = list("BEING_REMOVED" = BEING_REMOVED, "IN_INVENTORY" = IN_INVENTORY, "FORCE_STRING_OVERRIDE" = FORCE_STRING_OVERRIDE, "NEEDS_PERMIT" = NEEDS_PERMIT)
|
||||
"item_flags" = list("BEING_REMOVED" = BEING_REMOVED, "IN_INVENTORY" = IN_INVENTORY, "FORCE_STRING_OVERRIDE" = FORCE_STRING_OVERRIDE, "NEEDS_PERMIT" = NEEDS_PERMIT),
|
||||
"admin_flags" = list("BUILDMODE" = R_BUILDMODE, "ADMIN" = R_ADMIN, "BAN" = R_BAN, "FUN" = R_FUN, "SERVER" = R_SERVER, "DEBUG" = R_DEBUG, "POSSESS" = R_POSSESS, "PERMISSIONS" = R_PERMISSIONS, "STEALTH" = R_STEALTH, "POLL" = R_POLL, "VAREDIT" = R_VAREDIT, "SOUNDS" = R_SOUNDS, "SPAWN" = R_SPAWN, "AUTOLOGIN" = R_AUTOLOGIN, "DBRANKS" = R_DBRANKS)
|
||||
))
|
||||
|
||||
@@ -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")
|
||||
|
||||
+21
-17
@@ -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 ..()
|
||||
|
||||
@@ -405,6 +409,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"
|
||||
|
||||
|
||||
@@ -850,7 +850,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
|
||||
product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!"
|
||||
req_access_txt = "5"
|
||||
products = list(/obj/item/reagent_containers/syringe = 12, /obj/item/reagent_containers/dropper = 3, /obj/item/stack/medical/gauze = 8, /obj/item/reagent_containers/pill/patch/styptic = 5, /obj/item/reagent_containers/pill/insulin = 10,
|
||||
/obj/item/reagent_containers/pill/patch/silver_sulf = 5, /obj/item/reagent_containers/glass/bottle/charcoal = 4, /obj/item/reagent_containers/spray/medical/sterilizer = 1,
|
||||
/obj/item/reagent_containers/pill/patch/silver_sulf = 5, /obj/item/reagent_containers/medspray/styptic = 2, /obj/item/reagent_containers/medspray/silver_sulf = 2, /obj/item/reagent_containers/glass/bottle/charcoal = 4, /obj/item/reagent_containers/medspray/sterilizine = 1,
|
||||
/obj/item/reagent_containers/glass/bottle/epinephrine = 4, /obj/item/reagent_containers/glass/bottle/morphine = 4, /obj/item/reagent_containers/glass/bottle/salglu_solution = 3,
|
||||
/obj/item/reagent_containers/glass/bottle/toxin = 3, /obj/item/reagent_containers/syringe/antiviral = 6, /obj/item/reagent_containers/pill/salbutamol = 2, /obj/item/device/healthanalyzer = 4, /obj/item/device/sensor_device = 2, /obj/item/pinpointer/crew = 2)
|
||||
contraband = list(/obj/item/reagent_containers/pill/tox = 3, /obj/item/reagent_containers/pill/morphine = 4, /obj/item/reagent_containers/pill/charcoal = 6)
|
||||
@@ -876,7 +876,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
|
||||
density = FALSE
|
||||
products = list(/obj/item/reagent_containers/syringe = 3, /obj/item/reagent_containers/pill/patch/styptic = 5,
|
||||
/obj/item/reagent_containers/pill/patch/silver_sulf = 5, /obj/item/reagent_containers/pill/charcoal = 2,
|
||||
/obj/item/reagent_containers/spray/medical/sterilizer = 1)
|
||||
/obj/item/reagent_containers/medspray/sterilizine = 1)
|
||||
contraband = list(/obj/item/reagent_containers/pill/tox = 2, /obj/item/reagent_containers/pill/morphine = 2)
|
||||
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
+125
-270
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]")
|
||||
@@ -1,145 +0,0 @@
|
||||
/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:125px;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:375px;'>PERMISSIONS</th>
|
||||
<th style='width:100%;'>VERB-OVERRIDES</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/rights = rights2text(D.rank.rights," ")
|
||||
if(!rights)
|
||||
rights = "*none*"
|
||||
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]'>[rights]</a></td>"
|
||||
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(0," ",D.rank.adds,D.rank.subs)]</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=900x650")
|
||||
|
||||
/datum/admins/proc/log_admin_rank_modification(adm_ckey, new_rank)
|
||||
if(CONFIG_GET(flag/admin_legacy_system))
|
||||
return
|
||||
|
||||
if(!usr.client)
|
||||
return
|
||||
|
||||
if (!check_rights(R_PERMISSIONS))
|
||||
return
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return
|
||||
|
||||
if(!adm_ckey || !new_rank)
|
||||
return
|
||||
|
||||
adm_ckey = ckey(adm_ckey)
|
||||
|
||||
if(!adm_ckey)
|
||||
return
|
||||
|
||||
if(!istext(adm_ckey) || !istext(new_rank))
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_get_admin = SSdbcore.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
|
||||
if(!query_get_admin.warn_execute())
|
||||
return
|
||||
|
||||
var/new_admin = 1
|
||||
var/admin_id
|
||||
while(query_get_admin.NextRow())
|
||||
new_admin = 0
|
||||
admin_id = text2num(query_get_admin.item[1])
|
||||
|
||||
if(new_admin)
|
||||
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin")]` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
|
||||
if(!query_add_admin.warn_execute())
|
||||
return
|
||||
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
|
||||
if(!query_add_admin_log.warn_execute())
|
||||
return
|
||||
to_chat(usr, "<span class='adminnotice'>New admin added.</span>")
|
||||
else
|
||||
if(!isnull(admin_id) && isnum(admin_id))
|
||||
var/datum/DBQuery/query_change_admin = SSdbcore.NewQuery("UPDATE `[format_table_name("admin")]` SET rank = '[new_rank]' WHERE id = [admin_id]")
|
||||
if(!query_change_admin.warn_execute())
|
||||
return
|
||||
var/datum/DBQuery/query_change_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
|
||||
if(!query_change_admin_log.warn_execute())
|
||||
return
|
||||
to_chat(usr, "<span class='adminnnotice'>Admin rank changed.</span>")
|
||||
|
||||
|
||||
/datum/admins/proc/log_admin_permission_modification(adm_ckey, new_permission)
|
||||
if(CONFIG_GET(flag/admin_legacy_system))
|
||||
return
|
||||
if(!usr.client)
|
||||
return
|
||||
if(check_rights(R_PERMISSIONS))
|
||||
return
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return
|
||||
|
||||
if(!adm_ckey || !istext(adm_ckey) || !isnum(new_permission))
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_get_perms = SSdbcore.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
|
||||
if(!query_get_perms.warn_execute())
|
||||
return
|
||||
|
||||
var/admin_id
|
||||
while(query_get_perms.NextRow())
|
||||
admin_id = text2num(query_get_perms.item[1])
|
||||
|
||||
if(!admin_id)
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_change_perms = SSdbcore.NewQuery("UPDATE `[format_table_name("admin")]` SET flags = [new_permission] WHERE id = [admin_id]")
|
||||
if(!query_change_perms.warn_execute())
|
||||
return
|
||||
var/datum/DBQuery/query_change_perms_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edit permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
|
||||
query_change_perms_log.warn_execute()
|
||||
@@ -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,7 @@
|
||||
<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=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>
|
||||
@@ -109,6 +110,7 @@
|
||||
|
||||
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 +169,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))
|
||||
@@ -466,7 +485,7 @@
|
||||
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 +632,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.")
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
if(!CheckAdminHref(href, href_list))
|
||||
return
|
||||
|
||||
citaTopic(href, href_list) //CITADEL EDIT, MENTORS
|
||||
|
||||
if(href_list["ahelp"])
|
||||
if(!check_rights(R_ADMIN, TRUE))
|
||||
return
|
||||
@@ -271,50 +273,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 +874,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 +1661,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
|
||||
|
||||
@@ -62,7 +62,6 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
|
||||
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"
|
||||
desc = "A digitally controlled 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))
|
||||
|
||||
@@ -87,7 +87,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
if(href_list["priv_msg"])
|
||||
cmd_admin_pm(href_list["priv_msg"],null)
|
||||
return
|
||||
|
||||
// Mentor PM
|
||||
if(href_list["mentor_msg"])
|
||||
if(CONFIG_GET(flag.mentors_mobname_only))
|
||||
@@ -239,16 +238,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()
|
||||
@@ -409,21 +415,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 +615,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 +631,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()
|
||||
@@ -735,7 +730,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
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 +748,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
|
||||
@@ -384,6 +384,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 +543,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 +555,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 +723,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.
|
||||
@@ -629,7 +627,6 @@
|
||||
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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -42,7 +42,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
|
||||
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()
|
||||
@@ -73,7 +72,6 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
|
||||
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)
|
||||
Reference in New Issue
Block a user