Merge remote-tracking branch 'refs/remotes/origin/master' into upstream-merge-35475

This commit is contained in:
deathride58
2018-03-09 14:18:47 -05:00
179 changed files with 3274 additions and 1999 deletions
+1
View File
@@ -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
+3
View File
@@ -81,6 +81,9 @@
#define ABOVE_LIGHTING_PLANE 16
#define ABOVE_LIGHTING_LAYER 16
#define BYOND_LIGHTING_PLANE 17
#define BYOND_LIGHTING_LAYER 17
//HUD layer defines
#define FULLSCREEN_PLANE 18
+1 -1
View File
@@ -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
-1
View File
@@ -26,7 +26,6 @@ require only minor tweaks.
#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
#define SPACERUIN_MAP_EDGE_PAD 15
#define ZLEVEL_SPACE_RUIN_COUNT 7
// traits
// boolean - marks a level as having that property if present
+1
View File
@@ -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 -1
View File
@@ -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
+2 -2
View File
@@ -13,7 +13,7 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
//socks
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list)
//lizard bodyparts (blizzard intensifies)
//bodypart accessories (blizzard intensifies)
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/lizard, GLOB.animated_tails_list_lizard)
@@ -29,7 +29,7 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, GLOB.animated_spines_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.r_wings_list,roundstart = TRUE)
//moffs
init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list)
//CIT CHANGES START HERE, ADDS SNOWFLAKE BODYPARTS AND MORE
+2
View File
@@ -111,6 +111,7 @@
"spines" = pick(GLOB.spines_list),
"body_markings" = pick(GLOB.body_markings_list),
"legs" = "Normal Legs",
"caps" = pick(GLOB.caps_list),
"moth_wings" = pick(GLOB.moth_wings_list),
"taur" = "None",
"mam_body_markings" = "None",
@@ -166,6 +167,7 @@
"womb_efficiency" = CUM_EFFICIENCY,
"womb_fluid" = "femcum",
"flavor_text" = ""))
/proc/random_hair_style(gender)
switch(gender)
if(MALE)
+25
View File
@@ -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
View File
@@ -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)
-2
View File
@@ -1,5 +1,3 @@
#define DEBUG //Enables byond profiling and full runtime logs - note, this may also be defined in your .dme file
//Enables in-depth debug messages to runtime log (used for debugging)
//#define TESTING //By using the testing("message") proc you can create debug-feedback for people with this
//uncommented, but not visible in the release version)
+131 -130
View File
@@ -1,130 +1,131 @@
//Preferences stuff
//Hairstyles
GLOBAL_LIST_EMPTY(hair_styles_list) //stores /datum/sprite_accessory/hair indexed by name
GLOBAL_LIST_EMPTY(hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(hair_styles_female_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_list) //stores /datum/sprite_accessory/facial_hair indexed by name
GLOBAL_LIST_EMPTY(facial_hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_female_list) //stores only hair names
//Underwear
GLOBAL_LIST_EMPTY(underwear_list) //stores /datum/sprite_accessory/underwear indexed by name
GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
GLOBAL_LIST_EMPTY(underwear_f) //stores only underwear name
//Undershirts
GLOBAL_LIST_EMPTY(undershirt_list) //stores /datum/sprite_accessory/undershirt indexed by name
GLOBAL_LIST_EMPTY(undershirt_m) //stores only undershirt name
GLOBAL_LIST_EMPTY(undershirt_f) //stores only undershirt name
//Socks
GLOBAL_LIST_EMPTY(socks_list) //stores /datum/sprite_accessory/socks indexed by name
//Lizard Bits (all datum lists indexed by name)
GLOBAL_LIST_EMPTY(body_markings_list)
GLOBAL_LIST_EMPTY(tails_list_lizard)
GLOBAL_LIST_EMPTY(animated_tails_list_lizard)
GLOBAL_LIST_EMPTY(snouts_list)
GLOBAL_LIST_EMPTY(horns_list)
GLOBAL_LIST_EMPTY(frills_list)
GLOBAL_LIST_EMPTY(spines_list)
GLOBAL_LIST_EMPTY(legs_list)
GLOBAL_LIST_EMPTY(animated_spines_list)
//Mutant Human bits
GLOBAL_LIST_EMPTY(tails_list_human)
GLOBAL_LIST_EMPTY(animated_tails_list_human)
GLOBAL_LIST_EMPTY(ears_list)
GLOBAL_LIST_EMPTY(wings_list)
GLOBAL_LIST_EMPTY(wings_open_list)
GLOBAL_LIST_EMPTY(r_wings_list)
GLOBAL_LIST_EMPTY(moth_wings_list)
GLOBAL_LIST_INIT(ghost_forms_with_directions_list, list("ghost")) //stores the ghost forms that support directional sprites
GLOBAL_LIST_INIT(ghost_forms_with_accessories_list, list("ghost")) //stores the ghost forms that support hair and other such things
GLOBAL_LIST_INIT(security_depts_prefs, list(SEC_DEPT_RANDOM, SEC_DEPT_NONE, SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))
//Backpacks
#define GBACKPACK "Grey Backpack"
#define GSATCHEL "Grey Satchel"
#define GDUFFELBAG "Grey Duffel Bag"
#define LSATCHEL "Leather Satchel"
#define DBACKPACK "Department Backpack"
#define DSATCHEL "Department Satchel"
#define DDUFFELBAG "Department Duffel Bag"
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, DDUFFELBAG, GBACKPACK, GSATCHEL, GDUFFELBAG, LSATCHEL))
//Uplink spawn loc
#define UPLINK_PDA "PDA"
#define UPLINK_RADIO "Radio"
#define UPLINK_PEN "Pen" //like a real spy!
GLOBAL_LIST_INIT(uplink_spawn_loc_list, list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN))
//Female Uniforms
GLOBAL_LIST_EMPTY(female_clothing_icons)
//radical shit
GLOBAL_LIST_INIT(hit_appends, list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF"))
GLOBAL_LIST_INIT(scarySounds, list('sound/weapons/thudswoosh.ogg','sound/weapons/taser.ogg','sound/weapons/armbomb.ogg','sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg','sound/voice/hiss5.ogg','sound/voice/hiss6.ogg','sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg','sound/items/welder.ogg','sound/items/welder2.ogg','sound/machines/airlock.ogg','sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg'))
// Reference list for disposal sort junctions. Set the sortType variable on disposal sort junctions to
// the index of the sort department that you want. For example, sortType set to 2 will reroute all packages
// tagged for the Cargo Bay.
/* List of sortType codes for mapping reference
0 Waste
1 Disposals
2 Cargo Bay
3 QM Office
4 Engineering
5 CE Office
6 Atmospherics
7 Security
8 HoS Office
9 Medbay
10 CMO Office
11 Chemistry
12 Research
13 RD Office
14 Robotics
15 HoP Office
16 Library
17 Chapel
18 Theatre
19 Bar
20 Kitchen
21 Hydroponics
22 Janitor
23 Genetics
*/
GLOBAL_LIST_INIT(TAGGERLOCATIONS, list("Disposals",
"Cargo Bay", "QM Office", "Engineering", "CE Office",
"Atmospherics", "Security", "HoS Office", "Medbay",
"CMO Office", "Chemistry", "Research", "RD Office",
"Robotics", "HoP Office", "Library", "Chapel", "Theatre",
"Bar", "Kitchen", "Hydroponics", "Janitor Closet","Genetics"))
GLOBAL_LIST_INIT(guitar_notes, flist("sound/guitar/"))
GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt") + "")
GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt" + ""))
GLOBAL_LIST_INIT(station_suffixes, world.file2list("strings/station_suffixes.txt"))
GLOBAL_LIST_INIT(greek_letters, world.file2list("strings/greek_letters.txt"))
GLOBAL_LIST_INIT(phonetic_alphabet, world.file2list("strings/phonetic_alphabet.txt"))
GLOBAL_LIST_INIT(numbers_as_words, world.file2list("strings/numbers_as_words.txt"))
/proc/generate_number_strings()
var/list/L[198]
for(var/i in 1 to 99)
L += "[i]"
L += "\Roman[i]"
return L
GLOBAL_LIST_INIT(station_numerals, greek_letters + phonetic_alphabet + numbers_as_words + generate_number_strings())
GLOBAL_LIST_INIT(admiral_messages, list("Do you know how expensive these stations are?","Stop wasting my time.","I was sleeping, thanks a lot.","Stand and fight you cowards!","You knew the risks coming in.","Stop being paranoid.","Whatever's broken just build a new one.","No.", "<i>null</i>","<i>Error: No comment given.</i>", "It's a good day to die!"))
//Preferences stuff
//Hairstyles
GLOBAL_LIST_EMPTY(hair_styles_list) //stores /datum/sprite_accessory/hair indexed by name
GLOBAL_LIST_EMPTY(hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(hair_styles_female_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_list) //stores /datum/sprite_accessory/facial_hair indexed by name
GLOBAL_LIST_EMPTY(facial_hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_female_list) //stores only hair names
//Underwear
GLOBAL_LIST_EMPTY(underwear_list) //stores /datum/sprite_accessory/underwear indexed by name
GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
GLOBAL_LIST_EMPTY(underwear_f) //stores only underwear name
//Undershirts
GLOBAL_LIST_EMPTY(undershirt_list) //stores /datum/sprite_accessory/undershirt indexed by name
GLOBAL_LIST_EMPTY(undershirt_m) //stores only undershirt name
GLOBAL_LIST_EMPTY(undershirt_f) //stores only undershirt name
//Socks
GLOBAL_LIST_EMPTY(socks_list) //stores /datum/sprite_accessory/socks indexed by name
//Lizard Bits (all datum lists indexed by name)
GLOBAL_LIST_EMPTY(body_markings_list)
GLOBAL_LIST_EMPTY(tails_list_lizard)
GLOBAL_LIST_EMPTY(animated_tails_list_lizard)
GLOBAL_LIST_EMPTY(snouts_list)
GLOBAL_LIST_EMPTY(horns_list)
GLOBAL_LIST_EMPTY(frills_list)
GLOBAL_LIST_EMPTY(spines_list)
GLOBAL_LIST_EMPTY(legs_list)
GLOBAL_LIST_EMPTY(animated_spines_list)
//Mutant Human bits
GLOBAL_LIST_EMPTY(tails_list_human)
GLOBAL_LIST_EMPTY(animated_tails_list_human)
GLOBAL_LIST_EMPTY(ears_list)
GLOBAL_LIST_EMPTY(wings_list)
GLOBAL_LIST_EMPTY(wings_open_list)
GLOBAL_LIST_EMPTY(r_wings_list)
GLOBAL_LIST_EMPTY(moth_wings_list)
GLOBAL_LIST_EMPTY(caps_list)
GLOBAL_LIST_INIT(ghost_forms_with_directions_list, list("ghost")) //stores the ghost forms that support directional sprites
GLOBAL_LIST_INIT(ghost_forms_with_accessories_list, list("ghost")) //stores the ghost forms that support hair and other such things
GLOBAL_LIST_INIT(security_depts_prefs, list(SEC_DEPT_RANDOM, SEC_DEPT_NONE, SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))
//Backpacks
#define GBACKPACK "Grey Backpack"
#define GSATCHEL "Grey Satchel"
#define GDUFFELBAG "Grey Duffel Bag"
#define LSATCHEL "Leather Satchel"
#define DBACKPACK "Department Backpack"
#define DSATCHEL "Department Satchel"
#define DDUFFELBAG "Department Duffel Bag"
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, DDUFFELBAG, GBACKPACK, GSATCHEL, GDUFFELBAG, LSATCHEL))
//Uplink spawn loc
#define UPLINK_PDA "PDA"
#define UPLINK_RADIO "Radio"
#define UPLINK_PEN "Pen" //like a real spy!
GLOBAL_LIST_INIT(uplink_spawn_loc_list, list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN))
//Female Uniforms
GLOBAL_LIST_EMPTY(female_clothing_icons)
//radical shit
GLOBAL_LIST_INIT(hit_appends, list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF"))
GLOBAL_LIST_INIT(scarySounds, list('sound/weapons/thudswoosh.ogg','sound/weapons/taser.ogg','sound/weapons/armbomb.ogg','sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg','sound/voice/hiss5.ogg','sound/voice/hiss6.ogg','sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg','sound/items/welder.ogg','sound/items/welder2.ogg','sound/machines/airlock.ogg','sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg'))
// Reference list for disposal sort junctions. Set the sortType variable on disposal sort junctions to
// the index of the sort department that you want. For example, sortType set to 2 will reroute all packages
// tagged for the Cargo Bay.
/* List of sortType codes for mapping reference
0 Waste
1 Disposals
2 Cargo Bay
3 QM Office
4 Engineering
5 CE Office
6 Atmospherics
7 Security
8 HoS Office
9 Medbay
10 CMO Office
11 Chemistry
12 Research
13 RD Office
14 Robotics
15 HoP Office
16 Library
17 Chapel
18 Theatre
19 Bar
20 Kitchen
21 Hydroponics
22 Janitor
23 Genetics
*/
GLOBAL_LIST_INIT(TAGGERLOCATIONS, list("Disposals",
"Cargo Bay", "QM Office", "Engineering", "CE Office",
"Atmospherics", "Security", "HoS Office", "Medbay",
"CMO Office", "Chemistry", "Research", "RD Office",
"Robotics", "HoP Office", "Library", "Chapel", "Theatre",
"Bar", "Kitchen", "Hydroponics", "Janitor Closet","Genetics"))
GLOBAL_LIST_INIT(guitar_notes, flist("sound/guitar/"))
GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt") + "")
GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt" + ""))
GLOBAL_LIST_INIT(station_suffixes, world.file2list("strings/station_suffixes.txt"))
GLOBAL_LIST_INIT(greek_letters, world.file2list("strings/greek_letters.txt"))
GLOBAL_LIST_INIT(phonetic_alphabet, world.file2list("strings/phonetic_alphabet.txt"))
GLOBAL_LIST_INIT(numbers_as_words, world.file2list("strings/numbers_as_words.txt"))
/proc/generate_number_strings()
var/list/L[198]
for(var/i in 1 to 99)
L += "[i]"
L += "\Roman[i]"
return L
GLOBAL_LIST_INIT(station_numerals, greek_letters + phonetic_alphabet + numbers_as_words + generate_number_strings())
GLOBAL_LIST_INIT(admiral_messages, list("Do you know how expensive these stations are?","Stop wasting my time.","I was sleeping, thanks a lot.","Stand and fight you cowards!","You knew the risks coming in.","Stop being paranoid.","Whatever's broken just build a new one.","No.", "<i>null</i>","<i>Error: No comment given.</i>", "It's a good day to die!"))
+2 -1
View File
@@ -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)
))
+5 -1
View File
@@ -346,7 +346,11 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
icon_state = "runed_sense2"
desc = "You can no longer sense your target's presence."
return
desc = "You are currently tracking [blood_target] in [get_area_name(blood_target)]."
if(isliving(blood_target))
var/mob/living/real_target = blood_target
desc = "You are currently tracking [real_target.real_name] in [get_area_name(blood_target)]."
else
desc = "You are currently tracking [blood_target] in [get_area_name(blood_target)]."
var/target_angle = Get_Angle(Q, P)
var/target_dist = get_dist(P, Q)
cut_overlays()
@@ -394,4 +394,4 @@
min_val = 0
/datum/config_entry/string/default_view
config_entry_value = "15x15"
config_entry_value = "15x15"
+4 -13
View File
@@ -3,7 +3,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
/datum/controller/global_vars
name = "Global Variables"
var/list/gvars_datum_protected_varlist
var/static/list/gvars_datum_protected_varlist
var/list/gvars_datum_in_built_vars
var/list/gvars_datum_init_order
@@ -20,18 +20,9 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
Initialize()
/datum/controller/global_vars/Destroy(force)
stack_trace("Some fucker qdel'd the global holder!")
if(!force)
return QDEL_HINT_LETMELIVE
QDEL_NULL(statclick)
gvars_datum_protected_varlist.Cut()
gvars_datum_in_built_vars.Cut()
GLOB = null
return ..()
/datum/controller/global_vars/Destroy()
//fuck off kevinz
return QDEL_HINT_IWILLGC
/datum/controller/global_vars/stat_entry()
if(!statclick)
+5 -3
View File
@@ -50,11 +50,13 @@ SUBSYSTEM_DEF(mapping)
preloadTemplates()
#ifndef LOWMEMORYMODE
// Create space ruin levels
while (space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT)
while (space_levels_so_far < config.space_ruin_levels)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
// and one level with no ruins
empty_space = add_new_zlevel("Empty Area [1 + space_levels_so_far]", list(ZTRAIT_LINKAGE = CROSSLINKED))
for (var/i in 1 to config.space_empty_levels)
++space_levels_so_far
empty_space = add_new_zlevel("Empty Area [space_levels_so_far]", list(ZTRAIT_LINKAGE = CROSSLINKED))
// and the transit level
transit = add_new_zlevel("Transit", list(ZTRAIT_TRANSIT = TRUE))
@@ -175,7 +177,7 @@ SUBSYSTEM_DEF(mapping)
#ifndef LOWMEMORYMODE
// TODO: remove this when the DB is prepared for the z-levels getting reordered
while (world.maxz < (5 - 1) && space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT)
while (world.maxz < (5 - 1) && space_levels_so_far < config.space_ruin_levels)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
@@ -171,6 +171,8 @@ SUBSYSTEM_DEF(persistence)
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
for(var/A in GLOB.trophy_cases)
var/obj/structure/displaycase/trophy/T = A
if (T.showpiece)
continue
T.added_roundstart = TRUE
var/trophy_data = pick_n_take(trophy_items)
@@ -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)
+2
View File
@@ -387,6 +387,8 @@ SUBSYSTEM_DEF(ticker)
captainless=0
if(player.mind.assigned_role != player.mind.special_role)
SSjob.EquipRank(N, player.mind.assigned_role, 0)
if(CONFIG_GET(flag/roundstart_traits))
SStraits.AssignTraits(player, N.client, TRUE)
CHECK_TICK
if(captainless)
for(var/mob/dead/new_player/N in GLOB.player_list)
+1 -1
View File
@@ -458,7 +458,7 @@ SUBSYSTEM_DEF(timer)
if (wait >= 1 && callback && callback.object && callback.object != GLOBAL_PROC && QDELETED(callback.object))
stack_trace("addtimer called with a callback assigned to a qdeleted object")
wait = max(wait, world.tick_lag)
wait = max(wait, 0)
if(wait >= INFINITY)
CRASH("Attempted to create timer with INFINITY delay")
+21 -17
View File
@@ -223,25 +223,27 @@
/datum/browser/modal/listpicker
var/valueslist = list()
/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox")
/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox", width, height, slidecolor)
if (!User)
return
var/output = {"<form><input type="hidden" name="src" value="[REF(src)]"><ul class="sparse">"}
if (inputtype == "checkbox" || inputtype == "radio")
for (var/i in values)
var/div_slider = slidecolor
if(!i["allowed_edit"])
div_slider = "locked"
output += {"<li>
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""]>
<div class="slider"></div>
<span>[i["name"]]</span>
</label>
</li>"}
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""][i["allowed_edit"] ? "" : " onclick='return false' onkeydown='return false'"]>
<div class="slider [div_slider ? "[div_slider]" : ""]"></div>
<span>[i["name"]]</span>
</label>
</li>"}
else
for (var/i in values)
output += {"<li><input id="name="[i["name"]]"" style="width: 50px" type="[type]" name="[i["name"]]" value="[i["value"]]">
<label for="[i["name"]]">[i["name"]]</label></li>"}
<label for="[i["name"]]">[i["name"]]</label></li>"}
output += {"</ul><div style="text-align:center">
<button type="submit" name="button" value="1" style="font-size:large;float:[( Button2 ? "left" : "right" )]">[Button1]</button>"}
@@ -252,7 +254,7 @@
output += {"<button type="submit" name="button" value="3" style="font-size:large;float:right">[Button3]</button>"}
output += {"</form></div>"}
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, 350, 350, src, StealFocus, Timeout)
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, width, height, src, StealFocus, Timeout)
set_content(output)
/datum/browser/modal/listpicker/Topic(href,href_list)
@@ -272,30 +274,32 @@
opentime = 0
close()
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox")
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
User = C.mob
else
return
var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype)
var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype, width, height, slidecolor)
A.open()
A.wait()
if (A.selectedbutton)
return list("button" = A.selectedbutton, "values" = A.valueslist)
/proc/input_bitfield(var/mob/User, title, bitfield, current_value)
/proc/input_bitfield(var/mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null)
if (!User || !(bitfield in GLOB.bitfields))
return
var/list/pickerlist = list()
for (var/i in GLOB.bitfields[bitfield])
var/can_edit = 1
if(!isnull(allowed_edit_list) && !(allowed_edit_list & GLOB.bitfields[bitfield][i]))
can_edit = 0
if (current_value & GLOB.bitfields[bitfield][i])
pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i))
pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
else
pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i))
var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist)
pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist, width = nwidth, height = nheight, slidecolor = nslidecolor)
if (islist(result))
if (result["button"] == 2) // If the user pressed the cancel button
return
+1
View File
@@ -109,6 +109,7 @@
return
..()
//Proc to use when you 100% want to try to infect someone (ignoreing protective clothing and such), as long as they aren't immune
/mob/living/proc/ForceContractDisease(datum/disease/D, make_copy = TRUE, del_on_fail = FALSE)
if(!CanContractDisease(D))
+3 -5
View File
@@ -109,6 +109,7 @@
A.symptoms += S.Copy()
A.properties = properties.Copy()
A.id = id
A.mutable = mutable
//this is a new disease starting over at stage 1, so processing is not copied
return A
@@ -166,13 +167,10 @@
var/the_id = GetDiseaseID()
if(!SSdisease.archive_diseases[the_id])
if(new_name)
AssignName()
SSdisease.archive_diseases[the_id] = src // So we don't infinite loop
SSdisease.archive_diseases[the_id] = Copy()
var/datum/disease/advance/A = SSdisease.archive_diseases[the_id]
name = A.name
if(new_name)
AssignName()
//Generate disease properties based on the effects. Returns an associated list.
/datum/disease/advance/proc/GenerateProperties()
+16
View File
@@ -18,6 +18,8 @@
var/map_file = "BoxStation.dmm"
var/traits = null
var/space_ruin_levels = 7
var/space_empty_levels = 1
var/minetype = "lavaland"
@@ -106,6 +108,20 @@
log_world("map_config traits is not a list!")
return
var/temp = json["space_ruin_levels"]
if (isnum(temp))
space_ruin_levels = temp
else if (!isnull(temp))
log_world("map_config space_ruin_levels is not a number!")
return
temp = json["space_empty_levels"]
if (isnum(temp))
space_empty_levels = temp
else if (!isnull(temp))
log_world("map_config space_empty_levels is not a number!")
return
if ("minetype" in json)
minetype = json["minetype"]
+36
View File
@@ -0,0 +1,36 @@
/datum/martial_art/mushpunch
name = "Mushroom Punch"
/datum/martial_art/mushpunch/basic_hit(mob/living/carbon/human/A, mob/living/carbon/human/D)
var/atk_verb
to_chat(A, "<span class='spider'>You begin to wind up an attack...</span>")
if(do_after(A, 25, target = D))
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
atk_verb = pick("punches", "smashes", "ruptures", "cracks")
D.visible_message("<span class='danger'>[A] [atk_verb] [D] with inhuman strength, sending [D.p_them()] flying backwards!</span>", \
"<span class='userdanger'>[A] [atk_verb] you with inhuman strength, sending you flying backwards!</span>")
D.apply_damage(rand(15,30), BRUTE)
playsound(get_turf(D), 'sound/effects/meteorimpact.ogg', 25, 1, -1)
var/throwtarget = get_edge_target_turf(A, get_dir(A, get_step_away(D, A)))
D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time.
D.Knockdown(20)
if(atk_verb)
add_logs(A, D, "[atk_verb] (Mushroom Punch)")
return TRUE
return FALSE
/obj/item/mushpunch
name = "mysterious mushroom"
desc = "<I>Sapienza Ophioglossoides</I>:An odd mushroom from the flesh of a mushroom person. it has apparently retained some innate power of it's owner, as it quivers with barely-contained POWER!"
icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
icon_state = "mycelium-angel"
/obj/item/mushpunch/attack_self(mob/living/carbon/human/user)
if(!istype(user) || !user)
return
var/message = "<span class='spider'>You devour [src], and a confluence of skill and power from the mushroom enhances your punches! You do need a short moment to charge these powerful punches.</span>"
to_chat(user, message)
var/datum/martial_art/mushpunch/mush = new(null)
mush.teach(user)
qdel(src)
visible_message("<span class='warning'>[user] devours [src].</span>")
+2
View File
@@ -85,6 +85,8 @@
if(backpack_contents)
for(var/path in backpack_contents)
var/number = backpack_contents[path]
if(!isnum(number))//Default to 1
number = 1
for(var/i=0,i<number,i++)
H.equip_to_slot_or_del(new path(H),slot_in_backpack)
+22 -3
View File
@@ -11,7 +11,7 @@
var/mob_trait //if applicable, apply and remove this mob trait
var/mob/living/trait_holder
/datum/trait/New(mob/living/trait_mob)
/datum/trait/New(mob/living/trait_mob, spawn_effects)
..()
if(!trait_mob || (human_only && !ishuman(trait_mob)) || trait_mob.has_trait_datum(type))
qdel(src)
@@ -23,9 +23,9 @@
trait_holder.add_trait(mob_trait, ROUNDSTART_TRAIT)
START_PROCESSING(SStraits, src)
add()
if(!SSticker.HasRoundStarted()) //on roundstart or on latejoin; latejoin code is in new_player.dm
if(spawn_effects)
on_spawn()
addtimer(CALLBACK(src, .proc/post_add), 30)
addtimer(CALLBACK(src, .proc/post_add), 30)
/datum/trait/Destroy()
STOP_PROCESSING(SStraits, src)
@@ -38,16 +38,25 @@
SStraits.trait_objects -= src
return ..()
/datum/trait/proc/transfer_mob(mob/living/to_mob)
trait_holder.roundstart_traits -= src
to_mob.roundstart_traits += src
trait_holder = to_mob
on_transfer()
/datum/trait/proc/add() //special "on add" effects
/datum/trait/proc/on_spawn() //these should only trigger when the character is being created for the first time, i.e. roundstart/latejoin
/datum/trait/proc/remove() //special "on remove" effects
/datum/trait/proc/on_process() //process() has some special checks, so this is the actual process
/datum/trait/proc/post_add() //for text, disclaimers etc. given after you spawn in with the trait
/datum/trait/proc/on_transfer() //code called when the trait is transferred to a new mob
/datum/trait/process()
if(QDELETED(trait_holder))
qdel(src)
return
if(trait_holder.stat == DEAD)
return
on_process()
/mob/living/proc/get_trait_string(medical) //helper string. gets a string of all the traits the mob has
@@ -67,6 +76,16 @@
return "None"
return dat.Join("<br>")
/mob/living/proc/cleanse_trait_datums() //removes all trait datums
for(var/V in roundstart_traits)
var/datum/trait/T = V
qdel(T)
/mob/living/proc/transfer_trait_datums(mob/living/to_mob)
for(var/V in roundstart_traits)
var/datum/trait/T = V
T.transfer_mob(to_mob)
/*
Commented version of Nearsighted to help you add your own traits
+1 -1
View File
@@ -26,7 +26,7 @@
desc = "You walk with a gentle step, making stepping on sharp objects quieter and less painful."
value = 1
mob_trait = TRAIT_LIGHT_STEP
gain_text = "<span class='notice'>You walk with a little more lithenessk.</span>"
gain_text = "<span class='notice'>You walk with a little more litheness.</span>"
lose_text = "<span class='danger'>You start tromping around like a barbarian.</span>"
+1 -1
View File
@@ -114,7 +114,7 @@
if(trait_holder.reagents.has_reagent("mindbreaker"))
trait_holder.hallucination = 0
return
if(prob(1)) //we'll all be mad soon enough
if(prob(2)) //we'll all be mad soon enough
madness()
/datum/trait/insanity/proc/madness(mad_fools)
+1 -1
View File
@@ -542,4 +542,4 @@
if(EMERGENCY_ESCAPED_OR_ENDGAMED)
SSticker.news_report = STATION_EVACUATED
if(SSshuttle.emergency.is_hijacked())
SSticker.news_report = SHUTTLE_HIJACK
SSticker.news_report = SHUTTLE_HIJACK
+1 -1
View File
@@ -9,7 +9,7 @@
anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 0
var/obj/item/device/radio/beacon/Beacon
var/obj/item/device/beacon/Beacon
/obj/machinery/bluespace_beacon/Initialize()
. = ..()
+6 -1
View File
@@ -126,7 +126,7 @@
return examine(user)
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions)
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/traits)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -198,6 +198,9 @@
if(H)
H.faction |= factions
for(var/V in traits)
new V(H)
H.set_cloned_appearance()
H.suiciding = FALSE
@@ -316,6 +319,7 @@
SPEAK("An emergency ejection of [clonemind.name] has occurred. Survival not guaranteed.")
to_chat(user, "<span class='notice'>You force an emergency ejection. </span>")
go_out()
mob_occupant.apply_vore_prefs()
else
return ..()
@@ -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)
+6 -2
View File
@@ -71,7 +71,7 @@
if(pod.occupant)
continue //how though?
if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"]))
if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["traits"]))
temp = "[R.fields["name"]] => <font class='good'>Cloning cycle in progress...</font>"
records -= R
@@ -409,7 +409,7 @@
else if(pod.occupant)
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"]))
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["traits"]))
temp = "[C.fields["name"]] => <font class='good'>Cloning cycle in progress...</font>"
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
records.Remove(C)
@@ -482,6 +482,10 @@
R.fields["blood_type"] = dna.blood_type
R.fields["features"] = dna.features
R.fields["factions"] = mob_occupant.faction
R.fields["traits"] = list()
for(var/V in mob_occupant.roundstart_traits)
var/datum/trait/T = V
R.fields["traits"] += T.type
if (!isnull(mob_occupant.mind)) //Save that mind so traitors can continue traitoring after cloning.
R.fields["mind"] = "[REF(mob_occupant.mind)]"
+1 -1
View File
@@ -75,4 +75,4 @@
return 0
if(B.scrambledcodes || B.emagged)
return 0
return ..()
return ..()
+1 -1
View File
@@ -160,7 +160,7 @@
var/list/L = list()
var/list/areaindex = list()
if(regime_set == "Teleporter")
for(var/obj/item/device/radio/beacon/R in GLOB.teleportbeacons)
for(var/obj/item/device/beacon/R in GLOB.teleportbeacons)
if(is_eligible(R))
var/area/A = get_area(R)
L[avoid_assoc_duplicate_keys(A.name, areaindex)] = R
+3 -2
View File
@@ -101,8 +101,6 @@
/obj/machinery/door/airlock/Initialize()
. = ..()
wires = new /datum/wires/airlock(src)
if (cyclelinkeddir)
cyclelinkairlock()
if(frequency)
set_frequency(frequency)
@@ -127,6 +125,8 @@
/obj/machinery/door/airlock/LateInitialize()
. = ..()
if (cyclelinkeddir)
cyclelinkairlock()
if(abandoned)
var/outcome = rand(1,100)
switch(outcome)
@@ -178,6 +178,7 @@
limit--
while(!FoundDoor && limit)
if (!FoundDoor)
log_world("### MAP WARNING, [src] at [get_area_name(src, TRUE)] [COORD(src)] failed to find a valid airlock to cyclelink with!")
return
FoundDoor.cyclelinkedairlock = src
cyclelinkedairlock = FoundDoor
+1 -1
View File
@@ -68,4 +68,4 @@
/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user)
if(stat & NOPOWER)
open(1)
open(1)
+1 -1
View File
@@ -104,7 +104,7 @@
// SINGULO BEACON SPAWNER
/obj/item/device/sbeacondrop
name = "suspicious beacon"
icon = 'icons/obj/radio.dmi'
icon = 'icons/obj/device.dmi'
icon_state = "beacon"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
@@ -178,3 +178,4 @@
priority = "Extreme"
else
priority = "Undetermined"
+2 -2
View File
@@ -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
+1
View File
@@ -19,3 +19,4 @@
/obj/mecha/combat/durand/RemoveActions(mob/living/user, human_occupant = 0)
..()
defense_action.Remove(user)
+1
View File
@@ -27,3 +27,4 @@
..()
switch_damtype_action.Remove(user)
phasing_action.Remove(user)
+3 -3
View File
@@ -193,9 +193,9 @@
var/turf/T = safepick(get_area_turfs(impact_area))
if(T)
// Calculate new position (searches through beacons in world)
var/obj/item/device/radio/beacon/chosen
var/obj/item/device/beacon/chosen
var/list/possible = list()
for(var/obj/item/device/radio/beacon/W in GLOB.teleportbeacons)
for(var/obj/item/device/beacon/W in GLOB.teleportbeacons)
possible += W
if(possible.len > 0)
@@ -218,7 +218,7 @@
var/y_distance = TO.y - FROM.y
var/x_distance = TO.x - FROM.x
for (var/atom/movable/A in urange(12, FROM )) // iterate thru list of mobs in the area
if(istype(A, /obj/item/device/radio/beacon))
if(istype(A, /obj/item/device/beacon))
continue // don't teleport beacons because that's just insanely stupid
if(A.anchored)
continue
+1 -1
View File
@@ -22,7 +22,7 @@
/obj/item/reagent_containers/food/snacks/egg)
/obj/effect/spawner/bundle/costume/gladiator
name = "gladitator costume spawner"
name = "gladiator costume spawner"
items = list(
/obj/item/clothing/under/gladiator,
/obj/item/clothing/head/helmet/gladiator)
@@ -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)
+1 -1
View File
@@ -648,4 +648,4 @@
item_state = "defibpaddles0"
req_defib = FALSE
#undef HALFWAYCRITDEATH
#undef HALFWAYCRITDEATH
+44
View File
@@ -0,0 +1,44 @@
/obj/item/device/beacon
name = "\improper tracking beacon"
desc = "A beacon used by a teleporter."
icon = 'icons/obj/device.dmi'
icon_state = "beacon"
item_state = "beacon"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/enabled = TRUE
var/renamed = FALSE
/obj/item/device/beacon/Initialize()
. = ..()
if (enabled)
GLOB.teleportbeacons += src
else
icon_state = "beacon-off"
/obj/item/device/beacon/Destroy()
GLOB.teleportbeacons.Remove(src)
return ..()
/obj/item/device/beacon/attack_self(mob/user)
enabled = !enabled
if (enabled)
icon_state = "beacon"
GLOB.teleportbeacons += src
else
icon_state = "beacon-off"
GLOB.teleportbeacons.Remove(src)
to_chat(user, "<span class='notice'>You [enabled ? "enable" : "disable"] the beacon.</span>")
return
/obj/item/device/beacon/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/pen)) // needed for things that use custom names like the locator
var/new_name = stripped_input(user, "What would you like the name to be?")
if(!user.canUseTopic(src, BE_CLOSE))
return
if(new_name)
name = new_name
renamed = TRUE
return
else
return ..()
@@ -1,32 +0,0 @@
/obj/item/device/radio/beacon
name = "tracking beacon"
desc = "A beacon used by a teleporter."
icon_state = "beacon"
item_state = "beacon"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/code = "electronic"
dog_fashion = null
/obj/item/device/radio/beacon/Initialize()
. = ..()
GLOB.teleportbeacons += src
/obj/item/device/radio/beacon/Destroy()
GLOB.teleportbeacons.Remove(src)
return ..()
/obj/item/device/radio/beacon/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode)
return
/obj/item/device/radio/beacon/verb/alter_signal(t as text)
set name = "Alter Beacon's Signal"
set category = "Object"
set src in usr
if ((usr.canmove && !( usr.restrained() )))
src.code = t
if (!( src.code ))
src.code = "beacon"
src.add_fingerprint(usr)
return
-1
View File
@@ -1,4 +1,3 @@
//CREATOR'S NOTE: DO NOT FUCKING GIVE THIS TO BOTANY!
/obj/item/hot_potato
name = "hot potato"
+8
View File
@@ -192,6 +192,14 @@
for(var/i in 1 to 7)
new /obj/item/reagent_containers/glass/beaker( src )
/obj/item/storage/box/medsprays
name = "box of medical sprayers"
desc = "A box full of medical sprayers, with unscrewable caps and precision spray heads."
/obj/item/storage/box/medsprays/PopulateContents()
for(var/i in 1 to 7)
new /obj/item/reagent_containers/medspray( src )
/obj/item/storage/box/injectors
name = "box of DNA injectors"
desc = "This box contains injectors, it seems."
+25 -38
View File
@@ -17,9 +17,6 @@
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/temp = null
var/frequency = FREQ_LOCATOR_IMPLANT
var/broadcasting = null
var/listening = 1
flags_1 = CONDUCT_1
w_class = WEIGHT_CLASS_SMALL
item_state = "electronic"
@@ -32,17 +29,11 @@
/obj/item/locator/attack_self(mob/user)
user.set_machine(src)
var/dat
if (src.temp)
dat = "[src.temp]<BR><BR><A href='byond://?src=[REF(src)];temp=1'>Clear</A>"
if (temp)
dat = "[temp]<BR><BR><A href='byond://?src=[REF(src)];temp=1'>Clear</A>"
else
dat = {"
<B>Persistent Signal Locator</B><HR>
Frequency:
<A href='byond://?src=[REF(src)];freq=-10'>-</A>
<A href='byond://?src=[REF(src)];freq=-2'>-</A> [format_frequency(src.frequency)]
<A href='byond://?src=[REF(src)];freq=2'>+</A>
<A href='byond://?src=[REF(src)];freq=10'>+</A><BR>
<A href='?src=[REF(src)];refresh=1'>Refresh</A>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
@@ -59,30 +50,30 @@ Frequency:
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)))
usr.set_machine(src)
if (href_list["refresh"])
src.temp = "<B>Persistent Signal Locator</B><HR>"
temp = "<B>Persistent Signal Locator</B><HR>"
var/turf/sr = get_turf(src)
if (sr)
src.temp += "<B>Located Beacons:</B><BR>"
for(var/obj/item/device/radio/beacon/W in GLOB.teleportbeacons)
if (W.frequency == src.frequency)
var/turf/tr = get_turf(W)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 5)
direct = "very strong"
temp += "<B>Beacon Signals:</B><BR>"
for(var/obj/item/device/beacon/W in GLOB.teleportbeacons)
if (!W.renamed)
continue
var/turf/tr = get_turf(W)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 5)
direct = "very strong"
else
if (direct < 10)
direct = "strong"
else
if (direct < 10)
direct = "strong"
if (direct < 20)
direct = "weak"
else
if (direct < 20)
direct = "weak"
else
direct = "very weak"
src.temp += "[W.code]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
direct = "very weak"
temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
src.temp += "<B>Extranneous Signals:</B><BR>"
temp += "<B>Implant Signals:</B><BR>"
for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
if (!W.imp_in || !isliving(W.loc))
continue
@@ -103,18 +94,14 @@ Frequency:
direct = "strong"
else
direct = "weak"
src.temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
src.temp += "<B>You are at \[[sr.x],[sr.y],[sr.z]\]</B> in orbital coordinates.<BR><BR><A href='byond://?src=[REF(src)];refresh=1'>Refresh</A><BR>"
temp += "<B>You are at \[[sr.x],[sr.y],[sr.z]\]</B> in orbital coordinates.<BR><BR><A href='byond://?src=[REF(src)];refresh=1'>Refresh</A><BR>"
else
src.temp += "<B><FONT color='red'>Processing Error:</FONT></B> Unable to locate orbital position.<BR>"
temp += "<B><FONT color='red'>Processing Error:</FONT></B> Unable to locate orbital position.<BR>"
else
if (href_list["freq"])
src.frequency += text2num(href_list["freq"])
src.frequency = sanitize_frequency(src.frequency)
else
if (href_list["temp"])
src.temp = null
if (href_list["temp"])
temp = null
if (ismob(src.loc))
attack_self(src.loc)
else
+2 -2
View File
@@ -357,5 +357,5 @@
if(get_fuel() < max_fuel && nextrefueltick < world.time)
nextrefueltick = world.time + 10
reagents.add_reagent("welding_fuel", 1)
#undef WELDER_FUEL_BURN_INTERVAL
#undef WELDER_FUEL_BURN_INTERVAL
+3 -1
View File
@@ -42,6 +42,7 @@
icon_state = "waterballoon-e"
item_state = "balloon-empty"
/obj/item/toy/balloon/New()
create_reagents(10)
..()
@@ -286,6 +287,7 @@
w_class = WEIGHT_CLASS_SMALL
resistance_flags = FLAMMABLE
/obj/item/toy/windupToolbox
name = "windup toolbox"
desc = "A replica toolbox that rumbles when you turn the key."
@@ -332,7 +334,7 @@
/obj/item/toy/katana
name = "replica katana"
desc = "Woefully underpowered in D20. Almost has a sharp edge."
desc = "Woefully underpowered in D20."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "katana"
item_state = "katana"
@@ -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)
+12 -10
View File
@@ -15,9 +15,17 @@
var/openable = TRUE
var/obj/item/electronics/airlock/electronics
var/start_showpiece_type = null //add type for items on display
var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk")
var/trophy_message = ""
/obj/structure/displaycase/Initialize()
. = ..()
if(start_showpieces.len && !start_showpiece_type)
var/list/showpiece_entry = pick(start_showpieces)
if (showpiece_entry && showpiece_entry["type"])
start_showpiece_type = showpiece_entry["type"]
if (showpiece_entry["trophy_message"])
trophy_message = showpiece_entry["trophy_message"]
if(start_showpiece_type)
showpiece = new start_showpiece_type (src)
update_icon()
@@ -35,6 +43,9 @@
to_chat(user, "<span class='notice'>Hooked up with an anti-theft system.</span>")
if(showpiece)
to_chat(user, "<span class='notice'>There's [showpiece] inside.</span>")
if(trophy_message)
to_chat(user, "The plaque reads:")
to_chat(user, trophy_message)
/obj/structure/displaycase/proc/dump()
@@ -213,7 +224,7 @@
//The captains display case requiring specops ID access is intentional.
//The lab cage and captains display case do not spawn with electronics, which is why req_access is needed.
/obj/structure/displaycase/captain
alert = 1
alert = TRUE
start_showpiece_type = /obj/item/gun/energy/laser/captain
req_access = list(ACCESS_CENT_SPECOPS)
@@ -223,12 +234,9 @@
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
req_access = list(ACCESS_RD)
/obj/structure/displaycase/trophy
name = "trophy display case"
desc = "Store your trophies of accomplishment in here, and they will stay forever."
var/trophy_message = ""
var/placer_key = ""
var/added_roundstart = TRUE
var/is_locked = TRUE
@@ -245,12 +253,6 @@
GLOB.trophy_cases -= src
return ..()
/obj/structure/displaycase/trophy/examine(mob/user)
..()
if(trophy_message)
to_chat(user, "The plaque reads:")
to_chat(user, trophy_message)
/obj/structure/displaycase/trophy/attackby(obj/item/W, mob/user, params)
if(!user.Adjacent(src)) //no TK museology
+1 -1
View File
@@ -90,7 +90,7 @@
name = "magic mirror"
desc = "Turn and face the strange... face."
icon_state = "magic_mirror"
var/list/races_blacklist = list("skeleton", "agent", "angel", "military_synth", "memezombies", "clockwork golem servant", "android", "synth")
var/list/races_blacklist = list("skeleton", "agent", "angel", "military_synth", "memezombies", "clockwork golem servant", "android", "synth", "mush")
var/list/choosable_races = list()
/obj/structure/mirror/magic/New()
+14 -43
View File
@@ -1,6 +1,3 @@
/obj/structure/statue
name = "statue"
desc = "Placeholder. Yell at Firecage if you SOMEHOW see this."
@@ -16,47 +13,21 @@
/obj/structure/statue/attackby(obj/item/W, mob/living/user, params)
add_fingerprint(user)
user.changeNext_move(CLICK_CD_MELEE)
if(istype(W, /obj/item/wrench))
if(anchored)
user.visible_message("[user] is loosening the [name]'s bolts.", \
"<span class='notice'>You are loosening the [name]'s bolts...</span>")
if(W.use_tool(src, user, 40, volume=100))
if(!anchored)
return
user.visible_message("[user] loosened the [name]'s bolts!", \
"<span class='notice'>You loosen the [name]'s bolts!</span>")
anchored = FALSE
else
if(!isfloorturf(src.loc))
user.visible_message("<span class='warning'>A floor must be present to secure the [name]!</span>")
return
user.visible_message("[user] is securing the [name]'s bolts...", \
"<span class='notice'>You are securing the [name]'s bolts...</span>")
if(W.use_tool(src, user, 40, volume=100))
if(anchored)
return
user.visible_message("[user] has secured the [name]'s bolts.", \
"<span class='notice'>You have secured the [name]'s bolts.</span>")
anchored = TRUE
if(!(flags_1 & NODECONSTRUCT_1))
if(default_unfasten_wrench(user, W))
return
if(istype(W, /obj/item/weldingtool) || istype(W, /obj/item/gun/energy/plasmacutter))
if(!W.tool_start_check(user, amount=0))
return FALSE
else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
user.visible_message("[user] destroys the [name]!",
"<span class='notice'>You destroy the [name].</span>")
W.play_tool_sound(src)
qdel(src)
else if(istype(W, /obj/item/weldingtool) || istype(W, /obj/item/gun/energy/plasmacutter))
if(!W.tool_start_check(user, amount=0))
return FALSE
user.visible_message("[user] is slicing apart the [name].", \
"<span class='notice'>You are slicing apart the [name]...</span>")
if(W.use_tool(src, user, 40, volume=50))
user.visible_message("[user] slices apart the [name].", \
"<span class='notice'>You slice apart the [name]!</span>")
deconstruct(TRUE)
else
return ..()
user.visible_message("[user] is slicing apart the [name].", \
"<span class='notice'>You are slicing apart the [name]...</span>")
if(W.use_tool(src, user, 40, volume=50))
user.visible_message("[user] slices apart the [name].", \
"<span class='notice'>You slice apart the [name]!</span>")
deconstruct(TRUE)
return
return ..()
/obj/structure/statue/attack_hand(mob/living/user)
user.changeNext_move(CLICK_CD_MELEE)
+1 -1
View File
@@ -826,7 +826,7 @@
//returns 1 to let the dragdrop code know we are trapping this event
//returns 0 if we don't plan to trap the event
/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/living/tomob)
/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/tomob)
//this is the exact two check rights checks required to edit a ckey with vv.
if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0))
+128 -270
View File
@@ -1,13 +1,17 @@
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks)
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank
var/name = "NoRank"
var/rights = R_DEFAULT
var/list/adds
var/list/subs
var/exclude_rights = 0
var/include_rights = 0
var/can_edit_rights = 0
/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs)
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -17,19 +21,18 @@ GLOBAL_PROTECT(admin_ranks)
CRASH("Admin proc call creation of admin datum")
return
name = init_name
switch(name)
if("Removed",null,"")
QDEL_IN(src, 0)
throw EXCEPTION("invalid admin-rank name")
return
if(!name)
qdel(src)
throw EXCEPTION("Admin rank created without name.")
return
if(init_rights)
rights = init_rights
if(!init_adds)
init_adds = list()
if(!init_subs)
init_subs = list()
adds = init_adds
subs = init_subs
include_rights = rights
if(init_exclude_rights)
exclude_rights = init_exclude_rights
rights &= ~exclude_rights
if(init_edit_rights)
can_edit_rights = init_edit_rights
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
@@ -39,6 +42,9 @@ GLOBAL_PROTECT(admin_ranks)
return QDEL_HINT_LETMELIVE
. = ..()
/datum/admin_rank/can_vv_get(var_name)
return FALSE
/datum/admin_rank/vv_edit_var(var_name, var_value)
return FALSE
@@ -75,13 +81,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 +99,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 +256,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 +275,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()
+6 -9
View File
@@ -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]")
+8 -1
View File
@@ -1,5 +1,7 @@
GLOBAL_LIST_EMPTY(admin_datums)
GLOBAL_PROTECT(admin_datums)
GLOBAL_LIST_EMPTY(protected_admins)
GLOBAL_PROTECT(protected_admins)
GLOBAL_VAR_INIT(href_token, GenerateToken())
GLOBAL_PROTECT(href_token)
@@ -26,7 +28,7 @@ GLOBAL_PROTECT(href_token)
var/deadmined
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE)
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -51,6 +53,8 @@ GLOBAL_PROTECT(href_token)
if(R.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//only admins with +ADMIN start admined
if(protected)
GLOB.protected_admins[target] = src
if (force_active || (R.rights & R_AUTOLOGIN))
activate()
else
@@ -142,6 +146,9 @@ GLOBAL_PROTECT(href_token)
return 1 //we have all the rights they have and more
return 0
/datum/admins/can_vv_get(var_name, var_value)
return FALSE //nice try trialmin
/datum/admins/vv_edit_var(var_name, var_value)
return FALSE //nice try trialmin
+274
View File
@@ -0,0 +1,274 @@
/client/proc/edit_admin_permissions()
set category = "Admin"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS))
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
if(!check_rights(R_PERMISSIONS))
return
var/list/output = list({"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
<link rel='stylesheet' type='text/css' href='panels.css'>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;text-align:right;'>CKEY <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:40%;'>PERMISSIONS</th>
<th style='width:20%;'>DENIED</th>
<th style='width:40%;'>ALLOWED TO EDIT</th>
</tr>
"})
for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if(!D)
D = GLOB.deadmins[adm_ckey]
if (!D)
continue
var/deadminlink = ""
if (D.deadmined)
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.include_rights," ")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.exclude_rights," ", "-")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.can_edit_rights," ", "*")]</a></td>"
output += "</tr>"
output += {"
</table></div>
<div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div>
</body>
</html>"}
usr << browse(jointext(output, ""),"window=editrights;size=1000x650")
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
permissions_assets.send(src)
var/admin_ckey = ckey(href_list["ckey"])
var/datum/admins/D = GLOB.admin_datums[admin_ckey]
var/use_db
var/task = href_list["editrights"]
var/skip
if(task == "activate" || task == "deactivate")
skip = 1
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
if(admin_ckey in GLOB.protected_admins)
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>")
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if(D.rank in GLOB.protected_ranks)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
return
if(check_rights(R_DBRANKS, 0))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
use_db = "Temporary"
if(!use_db)
use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel")
if(use_db == "Cancel")
return
if(use_db == "Permanent")
use_db = 1
admin_ckey = sanitizeSQL(admin_ckey)
else
use_db = 0
if(task != "add")
D = GLOB.admin_datums[admin_ckey]
if(!D)
D = GLOB.deadmins[admin_ckey]
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
switch(task)
if("add")
admin_ckey = add_admin(use_db)
if(!admin_ckey)
return
change_admin_rank(admin_ckey, use_db)
if("remove")
remove_admin(admin_ckey, use_db, D)
if("rank")
change_admin_rank(admin_ckey, use_db, D)
if("permissions")
change_admin_flags(admin_ckey, use_db, D)
if("activate")
force_readmin(admin_ckey, D)
if("deactivate")
force_deadmin(admin_ckey, D)
edit_admin_permissions()
/datum/admins/proc/add_admin(use_db)
. = sanitizeSQL(ckey(input("New admin's ckey","Admin ckey") as text|null))
if(!.)
return 0
if(. in GLOB.admin_datums+GLOB.deadmins)
to_chat(usr, "<span class='danger'>[.] is already an admin.</span>")
return 0
if(use_db)
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')")
if(!query_add_admin.warn_execute())
return 0
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', 'New admin added: [.]')")
if(!query_add_admin_log.warn_execute())
return 0
/datum/admins/proc/remove_admin(admin_ckey, use_db, datum/admins/D)
if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it")
GLOB.admin_datums -= admin_ckey
GLOB.deadmins -= admin_ckey
D.disassociate()
if(use_db)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', 'Admin removed: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
message_admins("[key_name_admin(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/force_readmin(admin_ckey, datum/admins/D)
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully readmined [admin_ckey]")
/datum/admins/proc/force_deadmin(admin_ckey, datum/admins/D)
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [admin_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
/datum/admins/proc/change_admin_rank(admin_ckey, use_db, datum/admins/D)
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in GLOB.admin_ranks)
if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names
if(new_rank == "*New Rank*")
new_rank = sanitizeSQL(ckeyEx(input("Please input a new rank", "New custom rank") as text|null))
if(!new_rank)
return
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D)
R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
GLOB.admin_ranks += R
if(use_db)
if(!R)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_rights) VALUES ('[new_rank]', '0', '0', '0')")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', 'New rank added: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
var/old_rank
var/datum/DBQuery/query_get_rank = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_get_rank.warn_execute())
return
if(query_get_rank.NextRow())
old_rank = query_get_rank.item[1]
var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'")
if(!query_change_rank.warn_execute())
return
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')")
if(!query_change_rank_log.warn_execute())
return
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.rank = R //set the admin_rank as our rank
D.associate()
else
D = new(R, admin_ckey, TRUE) //new admin
message_admins("[key_name_admin(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/change_admin_flags(admin_ckey, use_db, datum/admins/D)
var/new_flags = input_bitfield(usr, "Include permission flags<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_flags))
return
var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags<br>Flags enabled here will be removed from a rank.<br>Note these take precedence over included flags.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.exclude_rights, 350, 660, "red", usr.client.holder.rank.can_edit_rights)
if(isnull(new_exclude_flags))
return
var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags<br>These are the flags this rank is allowed to edit if they have access to the permissions panel.<br>They will be unable to modify admins to a rank that has a flag not included here.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_can_edit_flags))
return
if(use_db)
var/old_flags
var/old_exclude_flags
var/old_can_edit_flags
var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'")
if(!query_get_rank_flags.warn_execute())
return
if(query_get_rank_flags.NextRow())
old_flags = text2num(query_get_rank_flags.item[1])
old_exclude_flags = text2num(query_get_rank_flags.item[2])
old_can_edit_flags = text2num(query_get_rank_flags.item[3])
var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'")
if(!query_change_rank_flags.warn_execute())
return
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', 'Permissions of [admin_ckey] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')")
if(!query_change_rank_flags_log.warn_execute())
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name != D.rank.name)
continue
R.rights = new_flags &= ~new_exclude_flags
R.exclude_rights = new_exclude_flags
R.include_rights = new_flags
R.can_edit_rights = new_can_edit_flags
for(var/i in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/A = GLOB.admin_datums[i]
if(!A)
A = GLOB.deadmins[i]
if (!A)
continue
if(A.rank.name != D.rank.name)
continue
var/client/C = GLOB.directory[A.target]
A.disassociate()
A.associate(C)
else
D.disassociate()
if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //duplicate our previous admin_rank but with a new name
//we don't add this clone to the admin_ranks list, as it is unique to that ckey
else
D.rank.rights = new_flags &= ~new_exclude_flags
D.rank.include_rights = new_flags
D.rank.exclude_rights = new_exclude_flags
var/client/C = GLOB.directory[admin_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")
log_admin("[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")
@@ -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()
+23 -4
View File
@@ -31,6 +31,7 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=tdomereset'>Reset Thunderdome to default state</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=set_name'>Rename Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=reset_name'>Reset Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=night_shift_set'>Set Night Shift Mode</A><BR>
<BR>
<B>Shuttles</B><BR>
<BR>
@@ -54,7 +55,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.")
+3 -51
View File
@@ -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>"
+7 -7
View File
@@ -235,42 +235,42 @@
if(ispath(type, /mob))
for(var/mob/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else if(ispath(type, /turf))
for(var/turf/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else if(ispath(type, /obj))
for(var/obj/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else if(ispath(type, /area))
for(var/area/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else if(ispath(type, /atom))
for(var/atom/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else if(ispath(type, /datum))
if(location == world) //snowflake for byond shortcut
for(var/datum/d) //stupid byond trick to have it not return atoms to make this less laggy
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
else
for(var/datum/d in location)
if(typecache[d.type])
if(typecache[d.type] && d.can_vv_get())
out += d
CHECK_TICK
+1 -1
View File
@@ -24,7 +24,7 @@
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol
var/res = alert(usr, "Show the title of this song to the players?",, "No", "Yes", "Cancel")
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res)
if("Yes")
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>")
@@ -242,14 +242,17 @@
return TRUE
/obj/effect/clockwork/sigil/transmission/update_icon()
var/power_charge = get_clockwork_power()
if(GLOB.ratvar_awakens)
alpha = 255
var/power_charge = get_clockwork_power()
alpha = min(initial(alpha) + power_charge * 0.02, 255)
if(!power_charge)
set_light(0)
else
set_light(max(alpha * 0.02, 1.4), max(alpha * 0.01, 0.1))
alpha = min(CEILING(initial(alpha) + power_charge * 0.02, 35), 255)
var/r = alpha * 0.02
var/p = max(alpha * 0.01, 0.1)
if(!power_charge && light_range != 0)
set_light(0)
else if(r != light_range || p != light_power)
set_light(r, p)
//Vitality Matrix: Drains health from non-servants to heal or even revive servants.
/obj/effect/clockwork/sigil/vitality
@@ -27,14 +27,13 @@
/datum/antagonist/disease/apply_innate_effects(mob/living/mob_override)
if(!istype(owner.current, /mob/camera/disease))
var/turf/T = get_turf(owner.current)
T = T ? T : locate(1, 1, 1)
T = T ? T : SSmapping.get_station_center()
var/mob/camera/disease/D = new /mob/camera/disease(T)
owner.transfer_to(D)
/datum/antagonist/disease/admin_add(datum/mind/new_owner,mob/admin)
..()
var/mob/camera/disease/D = new_owner.current
D.infect_patient_zero()
D.pick_name()
/datum/antagonist/disease/roundend_report()
@@ -17,11 +17,7 @@
var/mob/dead/observer/selected = pick_n_take(candidates)
var/mob/camera/disease/virus = new /mob/camera/disease(locate(1, 1, 1))
if(!virus.infect_patient_zero())
message_admins("Event attempted to spawn a sentient disease, but infection of patient zero failed.")
qdel(virus)
return WAITING_FOR_SOMETHING
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
virus.key = selected.key
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[key_name_admin(virus)] has been made into a sentient disease by an event.")
+88 -28
View File
@@ -12,14 +12,19 @@ the new instance inside the host to be updated to the template's stats.
icon = 'icons/mob/blob.dmi'
icon_state = "marker"
mouse_opacity = MOUSE_OPACITY_ICON
move_on_shuttle = 1
move_on_shuttle = FALSE
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = BELOW_MOB_LAYER
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
sight = SEE_SELF
sight = SEE_SELF|SEE_THRU
initial_language_holder = /datum/language_holder/empty
var/freemove = TRUE
var/freemove_end = 0
var/const/freemove_time = 1200
var/freemove_end_timerid
var/datum/action/innate/disease_adapt/adaptation_menu_action
var/datum/disease_ability/examining_ability
var/datum/browser/browser
@@ -45,19 +50,12 @@ the new instance inside the host to be updated to the template's stats.
/mob/camera/disease/Initialize(mapload)
.= ..()
adaptation_menu_action = new /datum/action/innate/disease_adapt()
adaptation_menu_action.Grant(src)
disease_instances = list()
hosts = list()
purchased_abilities = list()
unpurchased_abilities = list()
for(var/V in GLOB.disease_ability_singletons)
unpurchased_abilities[V] = TRUE
var/datum/disease_ability/A = V
if(A.start_with && A.CanBuy(src))
A.Buy(src, TRUE, FALSE)
disease_template = new /datum/disease/advance/sentient_disease()
disease_template.overmind = src
@@ -69,29 +67,45 @@ the new instance inside the host to be updated to the template's stats.
browser = new /datum/browser(src, "disease_menu", "Adaptation Menu", 1000, 770, src)
freemove_end = world.time + freemove_time
freemove_end_timerid = addtimer(CALLBACK(src, .proc/infect_random_patient_zero), freemove_time, TIMER_STOPPABLE)
/mob/camera/disease/Destroy()
. = ..()
QDEL_NULL(adaptation_menu_action)
for(var/V in GLOB.sentient_disease_instances)
var/datum/disease/advance/sentient_disease/S = V
if(S.overmind == src)
S.overmind = null
/mob/camera/disease/Login()
..()
if(freemove)
to_chat(src, "<span class='warning'>You have [round((freemove_end - world.time)/10)] seconds to select your first host. Click on a human to select your host.</span>")
/mob/camera/disease/Stat()
..()
if(statpanel("Status"))
stat("Adaptation Points: [points]/[total_points]")
stat("Hosts: [disease_instances.len]")
var/adapt_ready = next_adaptation_time - world.time
if(adapt_ready > 0)
stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s")
if(freemove)
stat("Host Selection Time: [round((freemove_end - world.time)/10)]s")
else
stat("Adaptation Points: [points]/[total_points]")
stat("Hosts: [disease_instances.len]")
var/adapt_ready = next_adaptation_time - world.time
if(adapt_ready > 0)
stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s")
/mob/camera/disease/say(message)
return
/mob/camera/disease/Move(NewLoc, Dir = 0)
if(world.time > (last_move_tick + move_delay))
follow_next(Dir & NORTHWEST)
last_move_tick = world.time
if(freemove)
forceMove(NewLoc)
else
if(world.time > (last_move_tick + move_delay))
follow_next(Dir & NORTHWEST)
last_move_tick = world.time
/mob/camera/disease/mind_initialize()
. = ..()
@@ -122,22 +136,56 @@ the new instance inside the host to be updated to the template's stats.
if(A)
A.disease_name = set_name
/mob/camera/disease/proc/infect_patient_zero()
var/list/possible_hosts = list()
var/datum/disease/advance/sentient_disease/V = disease_template.Copy()
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
if((H.stat != DEAD) && H.CanContractDisease(V))
possible_hosts += H
if(!possible_hosts.len)
/mob/camera/disease/proc/infect_random_patient_zero(del_on_fail = TRUE)
if(!freemove)
return FALSE
var/mob/living/carbon/human/H = pick(possible_hosts)
if(H.ForceContractDisease(V, FALSE, TRUE))
return TRUE
var/list/possible_hosts = list()
var/list/afk_possible_hosts = list()
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
var/turf/T = get_turf(H)
if((H.stat != DEAD) && T && is_station_level(T.z) && H.CanContractDisease(disease_template))
if(H.client && !H.client.is_afk())
possible_hosts += H
else
afk_possible_hosts += H
shuffle_inplace(possible_hosts)
shuffle_inplace(afk_possible_hosts)
possible_hosts += afk_possible_hosts //ideally we want a not-afk person, but we will settle for an afk one if there are no others (mostly for testing)
while(possible_hosts.len)
var/mob/living/carbon/human/target = possible_hosts[1]
if(force_infect(target))
return TRUE
possible_hosts.Cut(1, 2)
if(del_on_fail)
to_chat(src, "<span class=userdanger'>No hosts were available for your disease to infect.</span>")
qdel(src)
return FALSE
/mob/camera/disease/proc/force_infect(mob/living/L)
var/datum/disease/advance/sentient_disease/V = disease_template.Copy()
return L.ForceContractDisease(V, FALSE, TRUE)
var/result = L.ForceContractDisease(V, FALSE, TRUE)
if(result && freemove)
end_freemove()
return result
/mob/camera/disease/proc/end_freemove()
if(!freemove)
return
freemove = FALSE
move_on_shuttle = TRUE
adaptation_menu_action = new /datum/action/innate/disease_adapt()
adaptation_menu_action.Grant(src)
for(var/V in GLOB.disease_ability_singletons)
unpurchased_abilities[V] = TRUE
var/datum/disease_ability/A = V
if(A.start_with && A.CanBuy(src))
A.Buy(src, TRUE, FALSE)
if(freemove_end_timerid)
deltimer(freemove_end_timerid)
sight = SEE_SELF
/mob/camera/disease/proc/add_infection(datum/disease/advance/sentient_disease/V)
disease_instances += V
@@ -214,6 +262,18 @@ the new instance inside the host to be updated to the template's stats.
else
..()
/mob/camera/disease/ClickOn(var/atom/A, params)
if(freemove && ishuman(A))
var/mob/living/carbon/human/H = A
if(alert(src, "Select [H.name] as your initial host?", "Select Host", "Yes", "No") != "Yes")
return
if(!freemove)
return
if(QDELETED(H) || !force_infect(H))
to_chat(src, "<span class='warning'>[H ? H.name : "Host"] cannot be infected.</span>")
else
..()
/mob/camera/disease/proc/adapt_cooldown()
to_chat(src, "<span class='notice'>You have altered your genetic structure. You will be unable to adapt again for [adaptation_cooldown/10] seconds.</span>")
next_adaptation_time = world.time + adaptation_cooldown
@@ -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."
+3 -3
View File
@@ -57,7 +57,7 @@
continue // i'd be right happy to
meme_pack_data[P.group]["packs"] += list(list(
"name" = P.name,
"cost" = P.cost * 2, //displays twice the normal cost
"cost" = P.cost,
"id" = pack
))
@@ -120,12 +120,12 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
var/LZ = empty_turfs[rand(empty_turfs.len-1)]
SSshuttle.points -= SO.pack.cost * 2
SSshuttle.points -= SO.pack.cost
new /obj/effect/DPtarget(LZ, SO, podID)
. = TRUE
update_icon()
else
if(SO.pack.cost * (1.2*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^)
if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^)
landingzone = locate(pick(GLOB.the_station_areas)) in GLOB.sortedAreas
for(var/turf/open/floor/T in landingzone.contents)
if(is_blocked_turf(T))
+3 -2
View File
@@ -988,8 +988,9 @@
/obj/item/reagent_containers/pill/insulin,
/obj/item/stack/medical/gauze,
/obj/item/storage/box/beakers,
/obj/item/storage/box/medsprays,
/obj/item/storage/box/syringes,
/obj/item/storage/box/bodybags)
/obj/item/storage/box/bodybags)
crate_name = "medical supplies crate"
/datum/supply_pack/medical/vending
@@ -1983,4 +1984,4 @@
/obj/item/toy/redbutton,
/obj/item/toy/eightball,
/obj/item/vending_refill/donksoft)
crate_name = "toy crate"
crate_name = "toy crate"
+7 -2
View File
@@ -97,7 +97,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
if(!verify) // Can't access the asset cache browser, rip.
client.cache += unreceived
return 1
client.sending |= unreceived
var/job = ++client.last_asset_job
@@ -135,7 +135,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
else
concurrent_tracker++
send_asset(client, file, verify=FALSE)
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
@@ -350,6 +350,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
)
/datum/asset/simple/permissions
assets = list(
"padlock.png" = 'html/padlock.png'
)
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))
+30 -31
View File
@@ -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))
@@ -158,6 +157,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOBAL_LIST_EMPTY(external_rsc_urls)
#endif
/client/can_vv_get(var_name)
return var_name != NAMEOF(src, holder) && ..()
/client/vv_edit_var(var_name, var_value)
return var_name != NAMEOF(src, holder) && ..()
/client/New(TopicData)
var/tdata = TopicData //save this for later use
@@ -239,16 +243,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 +420,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 +620,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 +636,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 +735,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 +753,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!
+6
View File
@@ -9,6 +9,12 @@
earliest_start = 30 MINUTES
gamemode_blacklist = list("nuclear")
/datum/round_event_control/pirates/preRunEvent()
if (!SSmapping.empty_space)
return EVENT_CANT_RUN
return ..()
/datum/round_event/pirates
startWhen = 60 //2 minutes to answer
var/datum/comm_message/threat
@@ -23,9 +23,6 @@
growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
reagents_add = list("morphine" = 0.35, "charcoal" = 0.35, "nutriment" = 0)
/obj/item/reagent_containers/food/snacks/grown/mushroom/reishi
seed = /obj/item/seeds/reishi
name = "reishi"
@@ -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()
+11
View File
@@ -0,0 +1,11 @@
/datum/language/mushroom
name = "Mushroom"
desc = "A language that consists of the sound of periodic gusts of spore-filled air being released."
speech_verb = "puffs"
ask_verb = "puffs inquisitively"
exclaim_verb = "poofs loudly"
whisper_verb = "puffs quietly"
key = "y"
sentence_chance = 0
default_priority = 80
syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf")
+1
View File
@@ -160,3 +160,4 @@ GLOBAL_LIST_EMPTY(z_is_planet)
. = ..()
var/turf/T = get_turf(src)
GLOB.z_is_planet["[T.z]"] = TRUE
@@ -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
@@ -28,7 +28,7 @@
/obj/item/device/wormhole_jaunter/proc/get_destinations(mob/user)
var/list/destinations = list()
for(var/obj/item/device/radio/beacon/B in GLOB.teleportbeacons)
for(var/obj/item/device/beacon/B in GLOB.teleportbeacons)
var/turf/T = get_turf(B)
if(is_station_level(T.z))
destinations += B
@@ -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)
@@ -1402,6 +1402,14 @@
/datum/sprite_accessory/legs/digitigrade_lizard
name = "Digitigrade Legs"
/datum/sprite_accessory/caps
icon = 'icons/mob/mutant_bodyparts.dmi'
color_src = HAIR
/datum/sprite_accessory/caps/round
name = "Round"
icon_state = "round"
/datum/sprite_accessory/moth_wings
icon = 'icons/mob/wings.dmi'
color_src = null
+10 -2
View File
@@ -288,8 +288,16 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(response != "Ghost")
return //didn't want to ghost after-all
ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
return
/mob/camera/verb/ghost()
set category = "OOC"
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return
ghostize(0)
/mob/dead/observer/Move(NewLoc, direct)
if(updatedir)
@@ -634,7 +642,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/MouseDrop(atom/over)
if(!usr || !over)
return
if (isobserver(usr) && usr.client.holder && isliving(over))
if (isobserver(usr) && usr.client.holder && (isliving(over) || iscameramob(over)) )
if (usr.client.holder.cmd_ghost_drag(src,over))
return
+4
View File
@@ -191,6 +191,10 @@
blood_data["real_name"] = real_name
blood_data["features"] = dna.features
blood_data["factions"] = faction
blood_data["traits"] = list()
for(var/V in roundstart_traits)
var/datum/trait/T = V
blood_data["traits"] += T.type
return blood_data
//get the id of the substance this mob use as blood.
@@ -683,6 +683,8 @@ GLOBAL_LIST_EMPTY(roundstart_races)
S = GLOB.legs_list[H.dna.features["legs"]]
if("moth_wings")
S = GLOB.moth_wings_list[H.dna.features["moth_wings"]]
if("caps")
S = GLOB.caps_list[H.dna.features["caps"]]
//Mammal Bodyparts
if("mam_tail")
@@ -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
@@ -53,7 +53,8 @@
miss_sound = 'sound/weapons/slashmiss.ogg'
liked_food = MEAT
disliked_food = TOXIC
meat = /obj/item/reagent_containers/food/snacks/carpmeat/aquatic
/datum/species/aquatic/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
H.endTailWag()
@@ -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
@@ -0,0 +1,60 @@
/datum/species/mush //mush mush codecuck
name = "Mushroomperson"
id = "mush"
mutant_bodyparts = list("caps")
default_features = list("caps" = "Round")
fixed_mut_color = "DBBF92"
hair_color = "FF4B19" //cap color, spot color uses eye color
nojumpsuit = TRUE
say_mod = "poofs" //what does a mushroom sound like
species_traits = list(MUTCOLORS, NOEYES, NO_UNDERWEAR)
inherent_traits = list(TRAIT_NOBREATH)
speedmod = 1.5 //faster than golems but not by much
punchdamagelow = 6
punchdamagehigh = 14
punchstunthreshold = 14 //about 44% chance to stun
no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_w_uniform)
burnmod = 1.25
heatmod = 1.5
mutanteyes = /obj/item/organ/eyes/night_vision/mushroom
use_skintones = FALSE
var/datum/martial_art/mushpunch/mush
/datum/species/mush/check_roundstart_eligible()
return FALSE //hard locked out of roundstart on the order of design lead kor, this can be removed in the future when planetstation is here OR SOMETHING but right now we have a problem with races.
/datum/species/mush/after_equip_job(datum/job/J, mob/living/carbon/human/H)
H.grant_language(/datum/language/mushroom) //pomf pomf
/datum/species/mush/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
if(ishuman(C))
var/mob/living/carbon/human/H = C
if(!H.dna.features["caps"])
H.dna.features["caps"] = "Round"
handle_mutant_bodyparts(H)
H.faction |= "mushroom"
mush = new(null)
mush.teach(H)
/datum/species/mush/on_species_loss(mob/living/carbon/C)
. = ..()
C.faction -= "mushroom"
mush.remove(C)
QDEL_NULL(mush)
/datum/species/mush/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(chem.id == "weedkiller")
H.adjustToxLoss(3)
H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM)
return TRUE
/datum/species/mush/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour)
forced_colour = FALSE
..()
+24 -1
View File
@@ -59,6 +59,29 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
"÷" = "cords"
))
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
if(chance <= 0)
return "..."
if(chance >= 100)
return original_msg
var/list
words = splittext(original_msg," ")
new_words = list()
var/new_msg = ""
for(var/w in words)
if(prob(chance))
new_words += "..."
if(!keep_words)
continue
new_words += w
new_msg = jointext(new_words," ")
return new_msg
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE)
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
@@ -381,4 +404,4 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if(.)
return .
. = ..()
. = ..()
@@ -106,10 +106,10 @@
var/turf/t = turf
if(obscuredTurfs[t])
if(!t.obscured)
t.obscured = image('icons/effects/cameravis.dmi', t, null, LIGHTING_LAYER+1)
t.obscured = image('icons/effects/cameravis.dmi', t, null, BYOND_LIGHTING_LAYER+0.1)
t.obscured.pixel_x = -t.pixel_x
t.obscured.pixel_y = -t.pixel_y
t.obscured.plane = LIGHTING_PLANE+1
t.obscured.plane = BYOND_LIGHTING_PLANE+0.1
obscured += t.obscured
for(var/eye in seenby)
var/mob/camera/aiEye/m = eye
@@ -170,4 +170,4 @@
obscured += t.obscured
#undef UPDATE_BUFFER
#undef CHUNK_SIZE
#undef CHUNK_SIZE

Some files were not shown because too many files have changed in this diff Show More