module things, jfc

This commit is contained in:
Poojawa
2018-09-11 07:51:01 -05:00
parent 8b9ef1e400
commit 284e9d0325
695 changed files with 11343 additions and 5661 deletions
+2 -2
View File
@@ -95,7 +95,7 @@
SStgui.close_user_uis(occupant, src)
vr_human.real_mind = human_occupant.mind
vr_human.ckey = human_occupant.ckey
to_chat(vr_human, "<span class='notice'>Transfer successful! you are now playing as [vr_human] in VR!</span>")
to_chat(vr_human, "<span class='notice'>Transfer successful! You are now playing as [vr_human] in VR!</span>")
else
if(allow_creating_vr_humans)
to_chat(occupant, "<span class='warning'>Virtual avatar not found, attempting to create one...</span>")
@@ -104,7 +104,7 @@
if(T)
SStgui.close_user_uis(occupant, src)
build_virtual_human(occupant, T, V.vr_outfit)
to_chat(vr_human, "<span class='notice'>Transfer successful! you are now playing as [vr_human] in VR!</span>")
to_chat(vr_human, "<span class='notice'>Transfer successful! You are now playing as [vr_human] in VR!</span>")
else
to_chat(occupant, "<span class='warning'>Virtual world misconfigured, aborting transfer</span>")
else
+23 -11
View File
@@ -80,8 +80,8 @@
var/client/banned_client = banned_mob?.client
var/banned_mob_guest_key = had_banned_mob && IsGuestKey(banned_mob.key)
banned_mob = null
var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
var/sql_ckey = sanitizeSQL(ckey)
var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
if(!query_add_ban_get_ckey.warn_execute())
qdel(query_add_ban_get_ckey)
return
@@ -123,9 +123,9 @@
adminwho += ", [C]"
reason = sanitizeSQL(reason)
var/sql_a_ckey = sanitizeSQL(a_ckey)
if(maxadminbancheck)
var/datum/DBQuery/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
var/datum/DBQuery/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[sql_a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
if(!query_check_adminban_amt.warn_execute())
qdel(query_check_adminban_amt)
return
@@ -143,7 +143,12 @@
computerid = "0"
if(!ip)
ip = "0.0.0.0"
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', INET_ATON('[ip]'), '[a_ckey]', '[a_computerid]', INET_ATON('[a_ip]'), '[who]', '[adminwho]')"
var/sql_job = sanitizeSQL(job)
var/sql_computerid = sanitizeSQL(computerid)
var/sql_ip = sanitizeSQL(ip)
var/sql_a_computerid = sanitizeSQL(a_computerid)
var/sql_a_ip = sanitizeSQL(a_ip)
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql)
if(!query_add_ban.warn_execute())
qdel(query_add_ban)
@@ -207,10 +212,11 @@
bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )"
else
bantype_sql = "bantype = '[bantype_str]'"
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
var/sql_ckey = sanitizeSQL(ckey)
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
if(job)
sql += " AND job = '[job]'"
var/sql_job = sanitizeSQL(job)
sql += " AND job = '[sql_job]'"
if(!SSdbcore.Connect())
return
@@ -252,7 +258,7 @@
to_chat(usr, "Cancelled")
return
var/datum/DBQuery/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]")
var/datum/DBQuery/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]")
if(!query_edit_ban_get_details.warn_execute())
qdel(query_edit_ban_get_details)
return
@@ -319,7 +325,7 @@
if(!check_rights(R_BAN))
return
var/sql = "SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey) FROM [format_table_name("ban")] WHERE id = [id]"
var/sql = "SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey) FROM [format_table_name("ban")] WHERE id = [id]"
if(!SSdbcore.Connect())
return
@@ -406,6 +412,12 @@
output += "<tr><td><b>IP:</b> <input type='text' name='dbbanaddip'></td>"
output += "<td><b>Computer id:</b> <input type='text' name='dbbanaddcid'></td></tr>"
output += "<tr><td><b>Duration:</b> <input type='text' name='dbbaddduration'></td>"
output += "<tr><td><b>Severity:</b><select name='dbbanaddseverity'>"
output += "<option value=''>--</option>"
output += "<option value='High'>High Severity</option>"
output += "<option value='Medium'>Medium Severity</option>"
output += "<option value='Minor'>Minor Severity</option>"
output += "<option value='None'>No Severity</option>"
output += "<td><b>Job:</b><select name='dbbanaddjob'>"
output += "<option value=''>--</option>"
for(var/j in get_all_jobs())
@@ -477,7 +489,7 @@
output += "<th width='15%'><b>OPTIONS</b></th>"
output += "</tr>"
var/limit = " LIMIT [bansperpage * page], [bansperpage]"
var/datum/DBQuery/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), unbanned, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]")
var/datum/DBQuery/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]")
if(!query_search_bans.warn_execute())
qdel(query_search_bans)
return
+2 -2
View File
@@ -75,8 +75,8 @@
if(computer_id)
cidquery = " OR computerid = '[computer_id]' "
var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
if(!query_ban_check.Execute())
var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
if(!query_ban_check.Execute(async = TRUE))
qdel(query_ban_check)
return
while(query_ban_check.NextRow())
+2 -2
View File
@@ -123,9 +123,9 @@ GLOBAL_PROTECT(Banlist)
if (temp)
WRITE_FILE(GLOB.Banlist["minutes"], bantimestamp)
if(!temp)
create_message("note", key, bannedby, "Permanently banned - [reason]", null, null, 0, 0)
create_message("note", key, bannedby, "Permanently banned - [reason]", null, null, 0, 0, null, 0, 0)
else
create_message("note", key, bannedby, "Banned for [minutes] minutes - [reason]", null, null, 0, 0)
create_message("note", key, bannedby, "Banned for [minutes] minutes - [reason]", null, null, 0, 0, null, 0, 0)
return 1
/proc/RemoveBan(foldername)
+9 -5
View File
@@ -29,7 +29,7 @@
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[M.client.ckey] || GLOB.deadmins[M.client.ckey]) ? "rank" : "add"];ckey=[M.key]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
body += "\[<A href='?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[M.client.ckey] || GLOB.deadmins[M.client.ckey]) ? "rank" : "add"];key=[M.key]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
if(CONFIG_GET(flag/use_exp_tracking))
body += "\[<A href='?_src_=holder;[HrefToken()];getplaytimewindow=[REF(M)]'>" + M.client.get_exp_living() + "</a>\]"
@@ -50,6 +50,10 @@
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=subtract;mob=[REF(M)]'>\[decrease\]</a> "
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=set;mob=[REF(M)]'>\[set\]</a> "
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=zero;mob=[REF(M)]'>\[zero\]</a>"
var/full_version = "Unknown"
if(M.client.byond_version)
full_version = "[M.client.byond_version].[M.client.byond_build ? M.client.byond_build : "xxx"]"
body += "<br>\[<b>Byond version:</b> [full_version]\]<br>"
body += "<br><br>\[ "
@@ -241,7 +245,7 @@
if(CHANNEL.is_admin_channel)
dat+="<B><FONT style='BACKGROUND-COLOR: LightGreen'><A href='?src=[REF(src)];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A></FONT></B><BR>"
else
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR></B>"
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR></B>"
dat+="<BR><HR><A href='?src=[REF(src)];[HrefToken()];ac_refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Back</A>"
if(2)
@@ -313,7 +317,7 @@
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_censor_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR>"
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_censor_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Cancel</A>"
if(11)
dat+="<B>Nanotrasen D-Notice Handler</B><HR>"
@@ -324,7 +328,7 @@
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_d_notice=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ()]<BR>"
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_d_notice=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Back</A>"
if(12)
@@ -546,7 +550,7 @@
/datum/admins/proc/toggleaooc()
set category = "Server"
set desc="Toggle dis bitch"
set desc="Toggle to bitch"
set name="Toggle Antag OOC"
toggle_aooc()
log_admin("[key_name(usr)] toggled Antagonist OOC.")
+44 -27
View File
@@ -42,9 +42,6 @@ GLOBAL_PROTECT(protected_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
@@ -74,7 +71,7 @@ GLOBAL_PROTECT(protected_ranks)
if("varedit")
flag = R_VAREDIT
if("everything","host","all")
flag = ALL
flag = R_EVERYTHING
if("sound","sounds")
flag = R_SOUNDS
if("spawn","create")
@@ -112,6 +109,22 @@ GLOBAL_PROTECT(protected_ranks)
if(flag)
return ((rank.rights & flag) == flag) //true only if right has everything in flag
/proc/sync_ranks_with_db()
set waitfor = FALSE
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
return
var/list/sql_ranks = list()
for(var/datum/admin_rank/R in GLOB.protected_ranks)
var/sql_rank = sanitizeSQL(R.name)
var/sql_flags = sanitizeSQL(R.include_rights)
var/sql_exclude_flags = sanitizeSQL(R.exclude_rights)
var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights)
sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]"))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
//load our rank - > rights associations
/proc/load_admin_ranks(dbfail, no_update)
if(IsAdminAdvancedProcCall())
@@ -139,14 +152,7 @@ GLOBAL_PROTECT(protected_ranks)
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
if(CONFIG_GET(flag/load_legacy_ranks_only))
if(!no_update)
var/list/sql_ranks = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks)
var/sql_rank = sanitizeSQL(R.name)
var/sql_flags = sanitizeSQL(R.include_rights)
var/sql_exclude_flags = sanitizeSQL(R.exclude_rights)
var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights)
sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]"))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE, blocking = TRUE)
sync_ranks_with_db()
else
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())
@@ -172,20 +178,23 @@ GLOBAL_PROTECT(protected_ranks)
qdel(query_load_admin_ranks)
//load ranks from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
if(!fexists(backup_file))
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return
var/list/json = json_decode(file2text(backup_file))
return FALSE
var/list/json = json_decode(backup_file)
for(var/J in json["ranks"])
var/skip
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == "[J]") //this rank was already loaded from txt override
continue
skip = TRUE
if(skip)
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
return json
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks)
@@ -210,7 +219,8 @@ GLOBAL_PROTECT(protected_ranks)
GLOB.admins.Cut()
GLOB.protected_admins.Cut()
GLOB.deadmins.Cut()
dbfail = load_admin_ranks(dbfail, no_update)
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
dbfail = backup_file_json != null
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
@@ -251,16 +261,23 @@ GLOBAL_PROTECT(protected_ranks)
qdel(query_load_admins)
//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
var/list/json = json_decode(file2text(backup_file))
for(var/J in json["admins"])
if(!backup_file_json)
if(backup_file_json != null)
//already tried
return
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return
backup_file_json = json_decode(backup_file)
for(var/J in backup_file_json["admins"])
var/skip
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[ckeyEx(json["admins"]["[J]"])], ckey("[J]"))
skip = TRUE
if(skip)
continue
new /datum/admins(rank_names[ckeyEx(backup_file_json["admins"]["[J]"])], ckey("[J]"))
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in GLOB.admin_datums)
+2
View File
@@ -72,6 +72,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
/client/proc/stop_sounds,
/client/proc/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs /*hides all our hideable adminverbs*/
/datum/admins/proc/open_borgopanel
)
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
@@ -161,6 +162,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,
/client/proc/reload_configuration,
/datum/admins/proc/create_or_modify_area,
)
GLOBAL_PROTECT(admin_verbs_possess)
+1
View File
@@ -30,6 +30,7 @@
C.jobbancache = list()
var/datum/DBQuery/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
if(!query_jobban_build_cache.warn_execute())
qdel(query_jobban_build_cache)
return
while(query_jobban_build_cache.NextRow())
C.jobbancache[query_jobban_build_cache.item[1]] = query_jobban_build_cache.item[2]
+3 -3
View File
@@ -28,7 +28,7 @@
return
last_irc_check = rtod
var/server = CONFIG_GET(string/server)
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]" //CIT CHANGE - obfuscates the current gamemode from players
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name], Mode: [GLOB.master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]"
/datum/tgs_chat_command/ahelp
name = "ahelp"
@@ -84,7 +84,7 @@ GLOBAL_LIST(round_end_notifiees)
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
return "[sender.mention], the round has already ended!"
LAZYINITLIST(GLOB.round_end_notifiees)
GLOB.round_end_notifiees[sender] = TRUE
GLOB.round_end_notifiees[sender.mention] = TRUE
return "I will notify [sender.mention] when the round ends."
/datum/tgs_chat_command/sdql
@@ -116,4 +116,4 @@ GLOBAL_LIST(round_end_notifiees)
/datum/tgs_chat_command/reload_admins/proc/ReloadAsync()
set waitfor = FALSE
load_admins()
load_admins()
+4 -4
View File
@@ -154,12 +154,12 @@
else
dat += "ETA: <a href='?_src_=holder;[HrefToken()];edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]</a><BR>"
dat += "<B>Continuous Round Status</B><BR>"
dat += "<a href='?_src_=holder;[HrefToken()];toggle_continuous=1'>[CONFIG_GET(keyed_flag_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]</a>"
if(CONFIG_GET(keyed_flag_list/continuous)[SSticker.mode.config_tag])
dat += ", <a href='?_src_=holder;[HrefToken()];toggle_midround_antag=1'>[CONFIG_GET(keyed_flag_list/midround_antag)[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]</a><BR>"
dat += "<a href='?_src_=holder;[HrefToken()];toggle_continuous=1'>[CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]</a>"
if(CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag])
dat += ", <a href='?_src_=holder;[HrefToken()];toggle_midround_antag=1'>[CONFIG_GET(keyed_list/midround_antag)[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]</a><BR>"
else
dat += "<BR>"
if(CONFIG_GET(keyed_flag_list/midround_antag)[SSticker.mode.config_tag])
if(CONFIG_GET(keyed_list/midround_antag)[SSticker.mode.config_tag])
dat += "Time limit: <a href='?_src_=holder;[HrefToken()];alter_midround_time_limit=1'>[CONFIG_GET(number/midround_antag_time_check)] minutes into round</a><BR>"
dat += "Living crew limit: <a href='?_src_=holder;[HrefToken()];alter_midround_life_limit=1'>[CONFIG_GET(number/midround_antag_life_check) * 100]% of crew alive</a><BR>"
dat += "If limits past: <a href='?_src_=holder;[HrefToken()];toggle_noncontinuous_behavior=1'>[SSticker.mode.round_ends_with_antag_death ? "End The Round" : "Continue As Extended"]</a><BR>"
+1
View File
@@ -43,6 +43,7 @@
var/checktime = text2num(query_validate_time.item[1])
if(!checktime)
to_chat(src, "Datetime entered is improperly formatted or not later than current server time.")
qdel(query_validate_time)
return
endtime = query_validate_time.item[1]
qdel(query_validate_time)
+2 -15
View File
@@ -102,19 +102,6 @@
qdel(src)
//Shuttle Build
/obj/effect/shuttle_build
name = "shuttle_build"
desc = "Some assembly required."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "syndballoon"
anchored = TRUE
/obj/effect/shuttle_build/New()
SSshuttle.emergency.initiate_docking(SSshuttle.getDock("emergency_home"))
qdel(src)
//Arena
/obj/effect/forcefield/arena_shuttle
@@ -127,7 +114,7 @@
for(var/obj/effect/landmark/shuttle_arena_safe/exit in GLOB.landmarks_list)
warp_points += exit
/obj/effect/forcefield/arena_shuttle/CollidedWith(atom/movable/AM)
/obj/effect/forcefield/arena_shuttle/Bumped(atom/movable/AM)
if(!isliving(AM))
return
@@ -158,7 +145,7 @@
timeleft = 0
var/list/warp_points = list()
/obj/effect/forcefield/arena_shuttle_entrance/CollidedWith(atom/movable/AM)
/obj/effect/forcefield/arena_shuttle_entrance/Bumped(atom/movable/AM)
if(!isliving(AM))
return
+1 -4
View File
@@ -137,7 +137,7 @@ GLOBAL_PROTECT(href_token)
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
if(!other)
return 1 //they have no rights
if(rank.rights == 65535)
if(rank.rights == R_EVERYTHING)
return 1 //we have all the rights
if(src == other)
return 1 //you always have more rights than yourself
@@ -146,9 +146,6 @@ 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
+2 -2
View File
@@ -44,7 +44,7 @@
pagecount++
output += "|"
var/limit = " LIMIT [logssperpage * page], [logssperpage]"
var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), operation, IF(ckey IS NULL, target, byond_key), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("player")] ON target = ckey[search] ORDER BY datetime DESC[limit]")
var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), operation, IF(ckey IS NULL, target, byond_key), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("player")] ON target = ckey[search] ORDER BY datetime DESC[limit]")
if(!query_search_admin_logs.warn_execute())
qdel(query_search_admin_logs)
return
@@ -59,7 +59,7 @@
qdel(query_search_admin_logs)
if(action == 2)
output += "<h3>Admin ckeys with invalid ranks</h3>"
var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL")
var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL")
if(!query_check_admin_errors.warn_execute())
qdel(query_check_admin_errors)
return
+101 -2
View File
@@ -8,8 +8,8 @@
<B>General Secrets</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=admin_log'>Admin Log</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=mentor_log'>Mentor Log</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=show_admins'>Show Admin List</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=mentor_log'>Mentor Log</A><BR>
<BR>
"}
@@ -66,6 +66,7 @@
<A href='?src=[REF(src)];[HrefToken()];secrets=blackout'>Break all lights</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=whiteout'>Fix all lights</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=floorlava'>The floor is lava! (DANGEROUS: extremely lame)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=customportal'>Spawn a custom portal storm</A><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=flipmovement'>Flip client movement directions</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=randommovement'>Randomize client movement directions</A><BR>
@@ -110,6 +111,7 @@
if("mentor_log")
CitadelMentorLogSecret()
if("show_admins")
var/dat = "<B>Current admins:</B><HR>"
if(GLOB.admin_datums)
@@ -475,7 +477,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
@@ -643,6 +645,77 @@
message_admins("[key_name_admin(usr)] has reset all movement keys.")
log_admin("[key_name(usr)] has reset all movement keys.")
if("customportal")
if(!check_rights(R_FUN))
return
var/list/settings = list(
"mainsettings" = list(
"typepath" = list("desc" = "Path to spawn", "type" = "datum", "path" = "/mob/living", "subtypesonly" = TRUE, "value" = /mob/living/simple_animal/hostile/poison/bees),
"humanoutfit" = list("desc" = "Outfit if human", "type" = "datum", "path" = "/datum/outfit", "subtypesonly" = TRUE, "value" = /datum/outfit),
"amount" = list("desc" = "Number per portal", "type" = "number", "value" = 1),
"portalnum" = list("desc" = "Number of total portals", "type" = "number", "value" = 10),
"offerghosts" = list("desc" = "Get ghosts to play mobs", "type" = "boolean", "value" = "No"),
"minplayers" = list("desc" = "Minimum number of ghosts", "type" = "number", "value" = 1),
"playersonly" = list("desc" = "Only spawn ghost-controlled mobs", "type" = "boolean", "value" = "No"),
"ghostpoll" = list("desc" = "Ghost poll question", "type" = "string", "value" = "Do you want to play as %TYPE% portal invader?"),
"delay" = list("desc" = "Time between portals, in deciseconds", "type" = "number", "value" = 50),
"color" = list("desc" = "Portal color", "type" = "color", "value" = "#00FF00"),
"playlightning" = list("desc" = "Play lightning sounds on announcement", "type" = "boolean", "value" = "Yes"),
"announce_players" = list("desc" = "Make an announcement", "type" = "boolean", "value" = "Yes"),
"announcement" = list("desc" = "Announcement", "type" = "string", "value" = "Massive bluespace anomaly detected en route to %STATION%. Brace for impact."),
)
)
message_admins("[key_name(usr)] is creating a custom portal storm...")
var/list/prefreturn = presentpreflikepicker(usr,"Customize Portal Storm", "Customize Portal Storm", Button1="Ok", width = 600, StealFocus = 1,Timeout = 0, settings=settings)
if (prefreturn["button"] == 1)
var/list/prefs = settings["mainsettings"]
if (prefs["amount"]["value"] < 1 || prefs["portalnum"]["value"] < 1)
to_chat(usr, "Number of portals and mobs to spawn must be at least 1")
return
var/mob/pathToSpawn = prefs["typepath"]["value"]
if (!ispath(pathToSpawn))
pathToSpawn = text2path(pathToSpawn)
if (!ispath(pathToSpawn))
to_chat(usr, "Invalid path [pathToSpawn]")
return
var/list/candidates = list()
if (prefs["offerghosts"]["value"] == "Yes")
candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR)
if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"])
message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]")
return
if (prefs["announce_players"]["value"] == "Yes")
portalAnnounce(prefs["announcement"]["value"], (prefs["playlightning"]["value"] == "Yes" ? TRUE : FALSE))
var/mutable_appearance/storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER)
storm.color = prefs["color"]["value"]
message_admins("[key_name_admin(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]")
log_admin("[key_name(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]")
var/outfit = prefs["humanoutfit"]["value"]
if (!ispath(outfit))
outfit = text2path(outfit)
for (var/i in 1 to prefs["portalnum"]["value"])
if (length(candidates)) // if we're spawning players, gotta be a little tricky and also not spawn players on top of NPCs
var/ghostcandidates = list()
for (var/j in 1 to min(prefs["amount"]["value"], length(candidates)))
ghostcandidates += pick_n_take(candidates)
addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, length(ghostcandidates), storm, ghostcandidates, outfit), i*prefs["delay"]["value"])
else if (prefs["playersonly"]["value"] != "Yes")
addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, prefs["amount"]["value"], storm, null, outfit), i*prefs["delay"]["value"])
if(E)
E.processing = FALSE
if(E.announceWhen>0)
@@ -653,3 +726,29 @@
log_admin("[key_name(usr)] used secret [item]")
if (ok)
to_chat(world, text("<B>A secret has been activated by []!</B>", usr.key))
/proc/portalAnnounce(announcement, playlightning)
set waitfor = 0
if (playlightning)
sound_to_playing_players('sound/magic/lightning_chargeup.ogg')
sleep(80)
priority_announce(replacetext(announcement, "%STATION%", station_name()))
if (playlightning)
sleep(20)
sound_to_playing_players('sound/magic/lightningbolt.ogg')
/proc/doPortalSpawn(turf/loc, mobtype, numtospawn, portal_appearance, players, humanoutfit)
for (var/i in 1 to numtospawn)
var/mob/spawnedMob = new mobtype(loc)
if (length(players))
var/mob/chosen = players[1]
if (chosen.client)
chosen.client.prefs.copy_to(spawnedMob)
spawnedMob.key = chosen.key
players -= chosen
if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit))
var/mob/living/carbon/human/H = spawnedMob
H.equipOutfit(humanoutfit)
var/turf/T = get_step(loc, SOUTHWEST)
flick_overlay_static(portal_appearance, T, 15)
playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), 1)
+195 -48
View File
@@ -1,10 +1,10 @@
/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse)
/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
if(!type)
return
var/target_ckey
var/target_ckey = ckey(target_key)
if(!target_key && (type == "note" || type == "message" || type == "watchlist entry"))
var/new_key = input(usr,"Who would you like to create a [type] for?","Enter a key or ckey",null) as null|text
if(!new_key)
@@ -23,13 +23,8 @@
target_key = new_key
if(QDELETED(usr))
return
if(!target_ckey)
if(target_key)
target_ckey = ckey(target_key)
else
return
if(target_ckey)
target_ckey = sanitizeSQL(ckey(target_ckey))
target_ckey = sanitizeSQL(target_ckey)
if(!target_key)
target_key = target_ckey
if(!admin_ckey)
@@ -59,7 +54,30 @@
secret = 0
else
return
var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]')")
if(isnull(expiry))
if(alert(usr, "Set an expiry time? Expired messages are hidden like deleted ones.", "Expiry time?", "Yes", "No", "Cancel") == "Yes")
var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons.", "Set expiry time", SQLtime()) as null|text
if(!expire_time)
return
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
if(!query_validate_expire_time.warn_execute())
qdel(query_validate_expire_time)
return
if(query_validate_expire_time.NextRow())
var/checktime = text2num(query_validate_expire_time.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
qdel(query_validate_expire_time)
return
expiry = query_validate_expire_time.item[1]
qdel(query_validate_expire_time)
if(type == "note" && isnull(note_severity))
note_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!note_severity)
return
note_severity = sanitizeSQL(note_severity)
var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]', [expiry ? "'[expiry]'" : "NULL"], [note_severity ? "'[note_severity]'" : "NULL"])")
var/pm = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]: [text]"
var/header = "[key_name_admin(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]"
if(!query_create_message.warn_execute())
@@ -88,7 +106,7 @@
var/text
var/user_key_name = key_name(usr)
var/user_name_admin = key_name_admin(usr)
var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_del_message.warn_execute())
qdel(query_find_del_message)
return
@@ -123,7 +141,7 @@
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_message.warn_execute())
qdel(query_find_edit_message)
return
@@ -151,6 +169,103 @@
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_message)
/proc/edit_message_expiry(message_id, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_expiry_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), expire_timestamp FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_expiry_message.warn_execute())
qdel(query_find_edit_expiry_message)
return
if(query_find_edit_expiry_message.NextRow())
var/type = query_find_edit_expiry_message.item[1]
var/target_key = query_find_edit_expiry_message.item[2]
var/admin_key = query_find_edit_expiry_message.item[3]
var/old_expiry = query_find_edit_expiry_message.item[4]
var/new_expiry
var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons. Enter -1 to remove expiry time.", "Set expiry time", old_expiry) as null|text
if(!expire_time)
qdel(query_find_edit_expiry_message)
return
if(expire_time == "-1")
new_expiry = "non-expiring"
else
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time_edit = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
if(!query_validate_expire_time_edit.warn_execute())
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
return
if(query_validate_expire_time_edit.NextRow())
var/checktime = text2num(query_validate_expire_time_edit.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
return
new_expiry = query_validate_expire_time_edit.item[1]
qdel(query_validate_expire_time_edit)
var/edit_text = sanitizeSQL("Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]<hr>")
var/datum/DBQuery/query_edit_message_expiry = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET expire_timestamp = [expire_time == "-1" ? "NULL" : "'[new_expiry]'"], lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
if(!query_edit_message_expiry.warn_execute())
qdel(query_edit_message_expiry)
qdel(query_find_edit_expiry_message)
return
qdel(query_edit_message_expiry)
log_admin_private("[kn] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]")
message_admins("[kna] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]")
if(browse)
browse_messages("[type]")
else
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_expiry_message)
/proc/edit_message_severity(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_note_severity = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), severity FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_note_severity.warn_execute())
qdel(query_find_edit_note_severity)
return
if(query_find_edit_note_severity.NextRow())
var/type = query_find_edit_note_severity.item[1]
var/target_key = query_find_edit_note_severity.item[2]
var/admin_key = query_find_edit_note_severity.item[3]
var/old_severity = query_find_edit_note_severity.item[4]
if(!old_severity)
old_severity = "NA"
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = sanitizeSQL(usr.ckey)
var/new_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("high", "medium", "minor", "none") //lowercase for edit log consistency
if(!new_severity)
qdel(query_find_edit_note_severity)
return
new_severity = sanitizeSQL(new_severity)
var/edit_text = sanitizeSQL("Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]<hr>")
var/datum/DBQuery/query_edit_note_severity = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET severity = '[new_severity]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
if(!query_edit_note_severity.warn_execute(async = TRUE))
qdel(query_edit_note_severity)
qdel(qdel(query_find_edit_note_severity))
return
qdel(query_edit_note_severity)
log_admin_private("[kn] has edited the severity of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]")
message_admins("[kna] has edited the severity time of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]")
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_note_severity)
/proc/toggle_message_secrecy(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
@@ -162,7 +277,7 @@
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_message_secret.warn_execute())
qdel(query_find_message_secret)
return
@@ -189,10 +304,10 @@
return
var/list/output = list()
var/ruler = "<hr style='background:#000000; border:0; height:3px'>"
var/list/navbar = list("<a href='?_src_=holder;[HrefToken()];nonalpha=1'>\[All\]</a>|<a href='?_src_=holder;[HrefToken()];nonalpha=2'>\[#\]</a>")
var/list/navbar = list("<a href='?_src_=holder;[HrefToken()];nonalpha=1'>All</a><a href='?_src_=holder;[HrefToken()];nonalpha=2'>#</a>")
for(var/letter in GLOB.alphabet)
navbar += "|<a href='?_src_=holder;[HrefToken()];showmessages=[letter]'>\[[letter]\]</a>"
navbar += "|<a href='?_src_=holder;[HrefToken()];showmemo=1'>\[Memos\]</a>|<a href='?_src_=holder;[HrefToken()];showwatch=1'>\[Watchlist\]</a>"
navbar += "<a href='?_src_=holder;[HrefToken()];showmessages=[letter]'>[letter]</a>"
navbar += "<a href='?_src_=holder;[HrefToken()];showmemo=1'>Memos</a><a href='?_src_=holder;[HrefToken()];showwatch=1'>Watchlist</a>"
navbar += "<br><form method='GET' name='search' action='?'>\
<input type='hidden' name='_src_' value='holder'>\
[HrefTokenFormField()]\
@@ -203,16 +318,16 @@
if(type == "memo" || type == "watchlist entry")
if(type == "memo")
output += "<h2><center>Admin memos</h2>"
output += "<a href='?_src_=holder;[HrefToken()];addmemo=1'>\[Add memo\]</a></center>"
output += "<a href='?_src_=holder;[HrefToken()];addmemo=1'>Add memo</a></center>"
else if(type == "watchlist entry")
output += "<h2><center>Watchlist entries</h2>"
output += "<a href='?_src_=holder;[HrefToken()];addwatchempty=1'>\[Add watchlist entry\]</a>"
output += "<a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a>"
if(filter)
output += "|<a href='?_src_=holder;[HrefToken()];showwatch=1'>\[Unfilter clients\]</a></center>"
output += "<a href='?_src_=holder;[HrefToken()];showwatch=1'>Unfilter clients</a></center>"
else
output += "|<a href='?_src_=holder;[HrefToken()];showwatchfilter=1'>\[Filter offline clients\]</a></center>"
output += "<a href='?_src_=holder;[HrefToken()];showwatchfilter=1'>Filter offline clients</a></center>"
output += ruler
var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, server, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0")
var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), targetckey, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), expire_timestamp FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)")
if(!query_get_type_messages.warn_execute())
qdel(query_get_type_messages)
return
@@ -229,12 +344,17 @@
var/timestamp = query_get_type_messages.item[6]
var/server = query_get_type_messages.item[7]
var/editor_key = query_get_type_messages.item[8]
var/expire_timestamp = query_get_type_messages.item[9]
output += "<b>"
if(type == "watchlist entry")
output += "[t_key] | "
output += "[timestamp] | [server] | [admin_key]</b>"
output += " <a href='?_src_=holder;[HrefToken()];deletemessageempty=[id]'>\[Delete\]</a>"
output += " <a href='?_src_=holder;[HrefToken()];editmessageempty=[id]'>\[Edit\]</a>"
output += "[timestamp] | [server] | [admin_key]"
if(expire_timestamp)
output += " | Expires [expire_timestamp]"
output += "</b>"
output += " <a href='?_src_=holder;[HrefToken()];editmessageexpiryempty=[id]'>Change Expiry Time</a>"
output += " <a href='?_src_=holder;[HrefToken()];deletemessageempty=[id]'>Delete</a>"
output += " <a href='?_src_=holder;[HrefToken()];editmessageempty=[id]'>Edit</a>"
if(editor_key)
output += " <font size='2'>Last edit by [editor_key] <a href='?_src_=holder;[HrefToken()];messageedits=[id]'>(Click here to see edit log)</a></font>"
output += "<br>[text]<hr style='background:#000000; border:0; height:1px'>"
@@ -242,7 +362,7 @@
if(target_ckey)
target_ckey = sanitizeSQL(target_ckey)
var/target_key
var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, server, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), DATEDIFF(NOW(), timestamp) AS `age`, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 ORDER BY timestamp DESC")
var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), DATEDIFF(NOW(), timestamp), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), expire_timestamp, severity FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY timestamp DESC")
if(!query_get_messages.warn_execute())
qdel(query_get_messages)
return
@@ -267,6 +387,8 @@
var/editor_key = query_get_messages.item[8]
var/age = text2num(query_get_messages.item[9])
target_key = query_get_messages.item[10]
var/expire_timestamp = query_get_messages.item[11]
var/severity = query_get_messages.item[12]
var/alphatext = ""
var/nsd = CONFIG_GET(number/note_stale_days)
var/nfd = CONFIG_GET(number/note_fresh_days)
@@ -280,21 +402,33 @@
alpha = 10
skipped = TRUE
alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];"
var/list/data = list("<p style='margin:0px;[alphatext]'> <b>[timestamp] | [server] | [admin_key]</b>")
var/list/data = list("<div style='margin:0px;[alphatext]'><p class='severity'>")
if(severity)
data += "<img src='[severity]_button.png' height='24' width='24'></img> "
data += "<b>[timestamp] | [server] | [admin_key][secret ? " | <i>- Secret</i>" : ""]"
if(expire_timestamp)
data += " | Expires [expire_timestamp]"
data += "</b></p><center>"
if(!linkless)
data += " <a href='?_src_=holder;[HrefToken()];deletemessage=[id]'>\[Delete\]</a>"
if(type == "note")
data += " <a href='?_src_=holder;[HrefToken()];secretmessage=[id]'>[secret ? "<b>\[Secret\]</b>" : "\[Not secret\]"]</a>"
if(severity)
data += "<a href='?_src_=holder;[HrefToken()];editmessageseverity=[id]'>[severity=="none" ? "No" : "[capitalize(severity)]"] Severity</a>"
else
data += "<a href='?_src_=holder;[HrefToken()];editmessageseverity=[id]'>N/A Severity</a>"
data += " <a href='?_src_=holder;[HrefToken()];editmessageexpiry=[id]'>Change Expiry Time</a>"
data += " <a href='?_src_=holder;[HrefToken()];deletemessage=[id]'>Delete</a>"
if(type == "note")
data += " <a href='?_src_=holder;[HrefToken()];secretmessage=[id]'>[secret ? "<b>Secret</b>" : "Not secret"]</a>"
if(type == "message sent")
data += " <font size='2'>Message has been sent</font>"
if(editor_key)
data += "|"
else
data += " <a href='?_src_=holder;[HrefToken()];editmessage=[id]'>\[Edit\]</a>"
data += " <a href='?_src_=holder;[HrefToken()];editmessage=[id]'>Edit</a>"
if(editor_key)
data += " <font size='2'>Last edit by [editor_key] <a href='?_src_=holder;[HrefToken()];messageedits=[id]'>(Click here to see edit log)</a></font>"
data += "<br>[text]</p><hr style='background:#000000; border:0; height:1px; [alphatext]'>"
data += "</div></center>"
data += "<p style='[alphatext]'>[text]</p><hr style='background:#000000; border:0; height:1px; [alphatext]'>"
switch(type)
if("message")
messagedata += data
@@ -305,36 +439,43 @@
if("note")
notedata += data
qdel(query_get_messages)
if(!target_key)
var/datum/DBQuery/query_get_message_key = SSdbcore.NewQuery("SELECT byond_key FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'")
if(!query_get_message_key.warn_execute())
qdel(query_get_message_key)
return
if(query_get_message_key.NextRow())
target_key = query_get_message_key.item[1]
qdel(query_get_message_key)
output += "<h2><center>[target_key]</center></h2><center>"
if(!linkless)
output += "<a href='?_src_=holder;[HrefToken()];addnote=[target_key]'>\[Add note\]</a>"
output += " <a href='?_src_=holder;[HrefToken()];addmessage=[target_key]'>\[Add message\]</a>"
output += " <a href='?_src_=holder;[HrefToken()];addwatch=[target_key]'>\[Add to watchlist\]</a>"
output += " <a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>\[Refresh page\]</a></center>"
output += "<a href='?_src_=holder;[HrefToken()];addnote=[target_key]'>Add note</a>"
output += " <a href='?_src_=holder;[HrefToken()];addmessage=[target_key]'>Add message</a>"
output += " <a href='?_src_=holder;[HrefToken()];addwatch=[target_key]'>Add to watchlist</a>"
output += " <a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>Refresh page</a></center>"
else
output += " <a href='?_src_=holder;[HrefToken()];showmessageckeylinkless=[target_ckey]'>\[Refresh page\]</a></center>"
output += " <a href='?_src_=holder;[HrefToken()];showmessageckeylinkless=[target_ckey]'>Refresh page</a></center>"
output += ruler
if(messagedata)
output += "<h4>Messages</h4>"
output += "<h2>Messages</h2>"
output += messagedata
if(watchdata)
output += "<h4>Watchlist</h4>"
output += "<h2>Watchlist</h2>"
output += watchdata
if(notedata)
output += "<h4>Notes</h4>"
output += "<h2>Notes</h2>"
output += notedata
if(!linkless)
if (agegate)
if (skipped) //the first skipped message is still shown so that we can put this link over it.
output += " <center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1' style='position: relative; top: -3em;'>\[Show [skipped] hidden messages\]</center>"
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1' style='position: relative; top: -3em;'>Show [skipped] hidden messages</a></center>"
else
output += " <center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1'>\[Show All\]</center>"
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1'>Show All</a></center>"
else
output += " <center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>\[Hide Old\]</center>"
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>Hide Old</a></center>"
if(index)
var/search
output += "<center><a href='?_src_=holder;[HrefToken()];addmessageempty=1'>\[Add message\]</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>\[Add watchlist entry\]</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>\[Add note\]</a></center>"
output += "<center><a href='?_src_=holder;[HrefToken()];addmessageempty=1'>Add message</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>Add note</a></center>"
output += ruler
if(!isnum(index))
index = sanitizeSQL(index)
@@ -345,7 +486,7 @@
search = "^\[^\[:alpha:\]\]"
else
search = "^[index]"
var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 ORDER BY targetckey")
var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY targetckey")
if(!query_list_messages.warn_execute())
qdel(query_list_messages)
return
@@ -354,12 +495,18 @@
return
var/index_ckey = query_list_messages.item[1]
var/index_key = query_list_messages.item[2]
if(!index_key)
index_key = index_ckey
output += "<a href='?_src_=holder;[HrefToken()];showmessageckey=[index_ckey]'>[index_key]</a><br>"
qdel(query_list_messages)
else if(!type && !target_ckey && !index)
output += "<center></a> <a href='?_src_=holder;[HrefToken()];addmessageempty=1'>\[Add message\]</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>\[Add watchlist entry\]</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>\[Add note\]</a></center>"
output += "<center><a href='?_src_=holder;[HrefToken()];addmessageempty=1'>Add message</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>Add note</a></center>"
output += ruler
usr << browse({"<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /></head><body>[jointext(output, "")]</body></html>"}, "window=browse_messages;size=900x500")
var/datum/browser/browser = new(usr, "Note panel", "Manage player notes", 1000, 500)
var/datum/asset/notes_assets = get_asset_datum(/datum/asset/simple/notes)
notes_assets.send(src)
browser.set_content(jointext(output, ""))
browser.open()
/proc/get_message_output(type, target_ckey)
if(!SSdbcore.Connect())
@@ -370,7 +517,7 @@
var/output
if(target_ckey)
target_ckey = sanitizeSQL(target_ckey)
var/query = "SELECT id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0"
var/query = "SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)"
if(type == "message" || type == "watchlist entry")
query += " AND targetckey = '[target_ckey]'"
var/datum/DBQuery/query_get_message_output = SSdbcore.NewQuery(query)
@@ -432,7 +579,7 @@
timestamp = query_convert_time.item[1]
qdel(query_convert_time)
if(ckey && notetext && timestamp && admin_ckey && server)
create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0)
create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0, null, 0, 0)
notesfile.cd = "/"
notesfile.dir.Remove(ckey)
+82 -35
View File
@@ -215,37 +215,38 @@
var/banduration = text2num(href_list["dbbaddduration"])
var/banjob = href_list["dbbanaddjob"]
var/banreason = href_list["dbbanreason"]
var/banseverity = href_list["dbbanaddseverity"]
switch(bantype)
if(BANTYPE_PERMA)
if(!banckey || !banreason)
to_chat(usr, "Not enough parameters (Requires ckey and reason).")
if(!banckey || !banreason || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, severity, and reason).")
return
banduration = null
banjob = null
if(BANTYPE_TEMP)
if(!banckey || !banreason || !banduration)
to_chat(usr, "Not enough parameters (Requires ckey, reason and duration).")
if(!banckey || !banreason || !banduration || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, reason, severity and duration).")
return
banjob = null
if(BANTYPE_JOB_PERMA)
if(!banckey || !banreason || !banjob)
to_chat(usr, "Not enough parameters (Requires ckey, reason and job).")
if(!banckey || !banreason || !banjob || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).")
return
banduration = null
if(BANTYPE_JOB_TEMP)
if(!banckey || !banreason || !banjob || !banduration)
to_chat(usr, "Not enough parameters (Requires ckey, reason and job).")
if(!banckey || !banreason || !banjob || !banduration || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).")
return
if(BANTYPE_ADMIN_PERMA)
if(!banckey || !banreason)
to_chat(usr, "Not enough parameters (Requires ckey and reason).")
if(!banckey || !banreason || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, severity and reason).")
return
banduration = null
banjob = null
if(BANTYPE_ADMIN_TEMP)
if(!banckey || !banreason || !banduration)
to_chat(usr, "Not enough parameters (Requires ckey, reason and duration).")
if(!banckey || !banreason || !banduration || !banseverity)
to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and duration).")
return
banjob = null
@@ -270,7 +271,7 @@
if(!DB_ban_record(bantype, playermob, banduration, banreason, banjob, bankey, banip, bancid ))
to_chat(usr, "<span class='danger'>Failed to apply ban.</span>")
return
create_message("note", bankey, null, banreason, null, null, 0, 0)
create_message("note", bankey, null, banreason, null, null, 0, 0, null, 0, banseverity)
else if(href_list["editrightsbrowser"])
edit_admin_permissions(0)
@@ -340,7 +341,7 @@
else if(href_list["toggle_continuous"])
if(!check_rights(R_ADMIN))
return
var/list/continuous = CONFIG_GET(keyed_flag_list/continuous)
var/list/continuous = CONFIG_GET(keyed_list/continuous)
if(!continuous[SSticker.mode.config_tag])
continuous[SSticker.mode.config_tag] = TRUE
else
@@ -353,7 +354,7 @@
if(!check_rights(R_ADMIN))
return
var/list/midround_antag = CONFIG_GET(keyed_flag_list/midround_antag)
var/list/midround_antag = CONFIG_GET(keyed_list/midround_antag)
if(!midround_antag[SSticker.mode.config_tag])
midround_antag[SSticker.mode.config_tag] = TRUE
else
@@ -599,6 +600,9 @@
var/reason = input(usr,"Please State Reason.","Reason") as message|null
if(!reason)
return
var/severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!severity)
return
if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, "appearance"))
to_chat(usr, "<span class='danger'>Failed to apply ban.</span>")
return
@@ -606,7 +610,7 @@
jobban_buildcache(M.client)
ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]")
log_admin_private("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]")
create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0)
create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0, null, 0, severity)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] appearance banned [key_name_admin(M)].</span>")
to_chat(M, "<span class='boldannounce'><BIG>You have been appearance banned by [usr.client.key].</BIG></span>")
to_chat(M, "<span class='boldannounce'>The reason is: [reason]</span>")
@@ -804,30 +808,35 @@
//Drones
if(jobban_isbanned(M, "drone"))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=drone;jobban4=[REF(M)]'><font color=red>Drone</font></a></td>"
if(jobban_isbanned(M, ROLE_DRONE))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_DRONE];jobban4=[REF(M)]'><font color=red>Drone</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=drone;jobban4=[REF(M)]'>Drone</a></td>"
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_DRONE];jobban4=[REF(M)]'>Drone</a></td>"
//Positronic Brains
if(jobban_isbanned(M, "posibrain"))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=posibrain;jobban4=[REF(M)]'><font color=red>Posibrain</font></a></td>"
if(jobban_isbanned(M, ROLE_POSIBRAIN))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_POSIBRAIN];jobban4=[REF(M)]'><font color=red>Posibrain</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=posibrain;jobban4=[REF(M)]'>Posibrain</a></td>"
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_POSIBRAIN];jobban4=[REF(M)]'>Posibrain</a></td>"
//Sentience Potion Spawn
if(jobban_isbanned(M, ROLE_SENTIENCE))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_SENTIENCE];jobban4=[REF(M)]'><font color=red>Sentience Potion Spawn</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_SENTIENCE];jobban4=[REF(M)]'>Sentience Potion Spawn</a></td>"
//Deathsquad
if(jobban_isbanned(M, "deathsquad"))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=deathsquad;jobban4=[REF(M)]'><font color=red>Deathsquad</font></a></td>"
if(jobban_isbanned(M, ROLE_DEATHSQUAD))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_DEATHSQUAD];jobban4=[REF(M)]'><font color=red>Deathsquad</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=deathsquad;jobban4=[REF(M)]'>Deathsquad</a></td>"
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_DEATHSQUAD];jobban4=[REF(M)]'>Deathsquad</a></td>"
//Lavaland roles
if(jobban_isbanned(M, "lavaland"))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=lavaland;jobban4=[REF(M)]'><font color=red>Lavaland</font></a></td>"
if(jobban_isbanned(M, ROLE_LAVALAND))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_LAVALAND];jobban4=[REF(M)]'><font color=red>Lavaland</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=lavaland;jobban4=[REF(M)]'>Lavaland</a></td>"
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_LAVALAND];jobban4=[REF(M)]'>Lavaland</a></td>"
dat += "</tr></table>"
@@ -894,6 +903,16 @@
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=alien;jobban4=[REF(M)]'>Alien</a></td>"
//Other Roles (black)
dat += "<table cellpadding='1' cellspacing='0' width='100%'>"
dat += "<tr bgcolor='000000'><th colspan='5'><a href='?src=[REF(src)];[HrefToken()];jobban3=otherroles;jobban4=[REF(M)]' style='color: white;'>Other Roles</a></th></tr><tr align='center'>"
//Mind Transfer Potion
if(jobban_isbanned(M, ROLE_MIND_TRANSFER))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_MIND_TRANSFER];jobban4=[REF(M)]'><font color=red>Mind Transfer Potion</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_MIND_TRANSFER];jobban4=[REF(M)]'>Mind Transfer Potion</a></td>"
dat += "</tr></table>"
usr << browse(dat, "window=jobban2;size=800x450")
return
@@ -953,11 +972,13 @@
continue
joblist += jobPos
if("ghostroles")
joblist += list(ROLE_PAI, "posibrain", "drone", "deathsquad", "lavaland")
joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE)
if("teamantags")
joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN)
if("convertantags")
joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN)
if("otherroles")
joblist += list(ROLE_MIND_TRANSFER)
else
joblist += href_list["jobban3"]
@@ -969,6 +990,7 @@
//Banning comes first
if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban.
var/severity = null
switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel"))
if("Yes")
var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null
@@ -978,7 +1000,9 @@
var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null
if(!reason)
return
severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!severity)
return
var/msg
for(var/job in notbannedlist)
if(!DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job))
@@ -992,7 +1016,7 @@
msg = job
else
msg += ", [job]"
create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0)
create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes.</span>")
to_chat(M, "<span class='boldannounce'><BIG>You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].</BIG></span>")
to_chat(M, "<span class='boldannounce'>The reason is: [reason]</span>")
@@ -1001,6 +1025,9 @@
return 1
if("No")
var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null
severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!severity)
return
if(reason)
var/msg
for(var/job in notbannedlist)
@@ -1015,7 +1042,7 @@
msg = job
else
msg += ", [job]"
create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0)
create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] banned [key_name_admin(M)] from [msg].</span>")
to_chat(M, "<span class='boldannounce'><BIG>You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].</BIG></span>")
to_chat(M, "<span class='boldannounce'>The reason is: [reason]</span>")
@@ -1140,6 +1167,24 @@
var/message_id = href_list["editmessageempty"]
edit_message(message_id, browse = 1)
else if(href_list["editmessageexpiry"])
if(!check_rights(R_ADMIN))
return
var/message_id = href_list["editmessageexpiry"]
edit_message_expiry(message_id)
else if(href_list["editmessageexpiryempty"])
if(!check_rights(R_ADMIN))
return
var/message_id = href_list["editmessageexpiryempty"]
edit_message_expiry(message_id, browse = 1)
else if(href_list["editmessageseverity"])
if(!check_rights(R_ADMIN))
return
var/message_id = href_list["editmessageseverity"]
edit_message_severity(message_id)
else if(href_list["secretmessage"])
if(!check_rights(R_ADMIN))
return
@@ -1204,7 +1249,9 @@
if(query_get_message_edits.NextRow())
var/edit_log = query_get_message_edits.item[1]
if(!QDELETED(usr))
usr << browse(edit_log,"window=noteedits")
var/datum/browser/browser = new(usr, "Note edits", "Note edits")
browser.set_content(jointext(edit_log, ""))
browser.open()
qdel(query_get_message_edits)
else if(href_list["newban"])
@@ -1365,10 +1412,10 @@
if(!ismob(M))
to_chat(usr, "this can only be used on instances of type /mob.")
var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins.
var/speech = input("What will [key_name(M)] say?", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins.
if(!speech)
return
M.say(speech)
M.say(speech, forced = "admin speech")
speech = sanitize(speech) // Nah, we don't trust them
log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]</span>")
+5 -4
View File
@@ -196,9 +196,10 @@
/proc/SDQL_testout(list/query_tree, indent = 0)
var/static/whitespace = "&nbsp;&nbsp;&nbsp; "
var/spaces = ""
for(var/s = 0, s < indent, s++)
spaces += " "
spaces += whitespace
for(var/item in query_tree)
if(istype(item, /list))
@@ -212,12 +213,12 @@
if(!isnum(item) && query_tree[item])
if(istype(query_tree[item], /list))
to_chat(usr, "[spaces] (")
to_chat(usr, "[spaces][whitespace](")
SDQL_testout(query_tree[item], indent + 2)
to_chat(usr, "[spaces] )")
to_chat(usr, "[spaces][whitespace])")
else
to_chat(usr, "[spaces] [query_tree[item]]")
to_chat(usr, "[spaces][whitespace][query_tree[item]]")
@@ -294,8 +294,8 @@
break
parse_error("Expected ',' or ']' after array assoc value, but found '[token(i)]'")
return i
i++
continue
i++
continue
temp_expression_list = list()
i = expression(i, temp_expression_list)
while(token(i) && token(i) != "]")
@@ -331,7 +331,7 @@
//string: ''' <some text> ''' | '"' <some text > '"'
/datum/SDQL_parser/proc/string(i, list/node)
if(copytext(token(i), 1, 2) in list("'", "\""))
node += copytext(token(i),2,-1)
node += token(i)
else
parse_error("Expected string but found '[token(i)]'")
return i + 1
+2 -3
View File
@@ -372,7 +372,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/msg = "<font color='red' size='4'><b>- AdminHelp marked as IC issue! -</b></font><br>"
msg += "<font color='red'><b>Losing is part of the game!</b></font><br>"
msg += "<font color='red'>It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round.</font>"
if(initiator)
to_chat(initiator, msg)
@@ -607,7 +606,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
message["key"] = comms_key
message += type
var/list/servers = CONFIG_GET(keyed_string_list/cross_server)
var/list/servers = CONFIG_GET(keyed_list/cross_server)
for(var/I in servers)
world.Export("[servers[I]]?[list2params(message)]")
@@ -686,7 +685,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/is_antag = 0
if(found.mind && found.mind.special_role)
is_antag = 1
founds += "Name: [found.name]([found.real_name]) Ckey: [found.key] [is_antag ? "(Antag)" : null] "
founds += "Name: [found.name]([found.real_name]) Key: [found.key] Ckey: [found.ckey] [is_antag ? "(Antag)" : null] "
msg += "[original_word]<font size='1' color='[is_antag ? "red" : "black"]'>(<A HREF='?_src_=holder;[HrefToken(TRUE)];adminmoreinfo=[REF(found)]'>?</A>|<A HREF='?_src_=holder;[HrefToken(TRUE)];adminplayerobservefollow=[REF(found)]'>F</A>)</font> "
continue
msg += "[original_word] "
+2 -3
View File
@@ -66,9 +66,8 @@
if(src.mob)
var/mob/A = src.mob
A.x = tx
A.y = ty
A.z = tz
var/turf/T = locate(tx,ty,tz)
A.forceMove(T)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Coordiate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]")
+3 -8
View File
@@ -9,16 +9,11 @@
if(!msg)
return
msg = emoji_parse(msg)
mob.log_talk(msg, LOG_ASAY)
log_talk(mob,"[key_name(src)] : [msg]",LOGASAY)
msg = keywords_lookup(msg)
if(check_rights(R_ADMIN,0))
msg = "<span class='admin'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: <span class='message linkify'>[msg]</span></span>"
to_chat(GLOB.admins, msg)
else
msg = "<span class='adminobserver'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: <span class='message linkify'>[msg]</span></span>"
to_chat(GLOB.admins, msg)
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: <span class='message linkify'>[msg]</span></span>"
to_chat(GLOB.admins, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+9 -10
View File
@@ -21,18 +21,17 @@
var/mob/living/silicon/robot/borg
var/user
/datum/borgpanel/New(user, mob/living/silicon/robot/borg)
if(!istype(borg))
/datum/borgpanel/New(to_user, mob/living/silicon/robot/to_borg)
if(!istype(to_borg))
CRASH("Borg panel is only available for borgs")
qdel(src)
if (istype(user, /mob))
var/mob/M = user
if (!M.client)
CRASH("Borg panel attempted to open to a mob without a client")
src.user = M.client
else
src.user = user
src.borg = borg
user = CLIENT_FROM_VAR(to_user)
if (!user)
CRASH("Borg panel attempted to open to a mob without a client")
borg = to_borg
/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+3 -6
View File
@@ -1,6 +1,6 @@
/client/proc/dsay(msg as text)
set category = "Special Verbs"
set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite
set name = "Dsay"
set hidden = 1
if(!src.holder)
to_chat(src, "Only administrators may use this command.")
@@ -15,16 +15,13 @@
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
log_talk(mob,"[key_name(src)] : [msg]",LOGDSAY)
mob.log_talk(msg, LOG_DSAY)
if (!msg)
return
msg = emoji_parse(msg)
var/static/nicknames = world.file2list("[global.config.directory]/admin_nicknames.txt")
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key])</span> says, <span class='message'>\"[msg]\"</span></span>"
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
for (var/mob/M in GLOB.player_list)
if(isnewplayer(M))
+53
View File
@@ -959,6 +959,50 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
to_chat(usr, "<span class='name'>[template.name]</span>")
to_chat(usr, "<span class='italics'>[template.description]</span>")
/client/proc/place_ruin()
set category = "Debug"
set name = "Spawn Ruin"
set desc = "Attempt to randomly place a specific ruin."
if (!holder)
return
var/list/exists = list()
for(var/landmark in GLOB.ruin_landmarks)
var/obj/effect/landmark/ruin/L = landmark
exists[L.ruin_template] = landmark
var/list/names = list()
names += "---- Space Ruins ----"
for(var/name in SSmapping.space_ruins_templates)
names[name] = list(SSmapping.space_ruins_templates[name], ZTRAIT_SPACE_RUINS, /area/space)
names += "---- Lava Ruins ----"
for(var/name in SSmapping.lava_ruins_templates)
names[name] = list(SSmapping.lava_ruins_templates[name], ZTRAIT_LAVA_RUINS, /area/lavaland/surface/outdoors/unexplored)
var/ruinname = input("Select ruin", "Spawn Ruin") as null|anything in names
var/data = names[ruinname]
if (!data)
return
var/datum/map_template/ruin/template = data[1]
if (exists[template])
var/response = alert("There is already a [template] in existence.", "Spawn Ruin", "Jump", "Place Another", "Cancel")
if (response == "Jump")
usr.forceMove(get_turf(exists[template]))
return
else if (response == "Cancel")
return
var/len = GLOB.ruin_landmarks.len
seedRuins(SSmapping.levels_by_trait(data[2]), max(1, template.cost), data[3], list(ruinname = template))
if (GLOB.ruin_landmarks.len > len)
var/obj/effect/landmark/ruin/landmark = GLOB.ruin_landmarks[GLOB.ruin_landmarks.len]
log_admin("[key_name(src)] randomly spawned ruin [ruinname] at [COORD(landmark)].")
usr.forceMove(get_turf(landmark))
to_chat(src, "<span class='name'>[template.name]</span>")
to_chat(src, "<span class='italics'>[template.description]</span>")
else
to_chat(src, "<span class='warning'>Failed to place [template.name].</span>")
/client/proc/clear_dynamic_transit()
set category = "Debug"
set name = "Clear Dynamic Turf Reservations"
@@ -1047,3 +1091,12 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
return
sort = sortlist[sort]
profile_show(src, sort)
/client/proc/reload_configuration()
set category = "Debug"
set name = "Reload Configuration"
set desc = "Force config reload to world default"
if(!check_rights(R_DEBUG))
return
if(alert(usr, "Are you absolutely sure you want to reload the configuration from the default path on the disk, wiping any in-round modificatoins?", "Really reset?", "No", "Yes") == "Yes")
config.admin_reload()
+43 -28
View File
@@ -1,29 +1,45 @@
/proc/show_individual_logging_panel(mob/M, source = LOGSRC_CLIENT, type = INDIVIDUAL_ATTACK_LOG)
if(!M || !ismob(M))
return
var/ntype = text2num(type)
//Add client links
var/dat = ""
if(M.client)
dat += "<center><p>Client</p></center>"
dat += "<center><a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_ATTACK_LOG];log_src=[LOGSRC_CLIENT]'>Attack log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_SAY_LOG];log_src=[LOGSRC_CLIENT]'>Say log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_EMOTE_LOG];log_src=[LOGSRC_CLIENT]'>Emote log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_OOC_LOG];log_src=[LOGSRC_CLIENT]'>OOC log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_SHOW_ALL_LOG];log_src=[LOGSRC_CLIENT]'>Show all</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[type];log_src=[LOGSRC_CLIENT]'>Refresh</a></center>"
if(M.client)
dat += "<center><p>Client</p></center>"
dat += "<center>"
dat += individual_logging_panel_link(M, INDIVIDUAL_ATTACK_LOG, LOGSRC_CLIENT, "Attack Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_SAY_LOG, LOGSRC_CLIENT, "Say Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_EMOTE_LOG, LOGSRC_CLIENT, "Emote Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_COMMS_LOG, LOGSRC_CLIENT, "Comms Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_OOC_LOG, LOGSRC_CLIENT, "OOC Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_SHOW_ALL_LOG, LOGSRC_CLIENT, "Show All", source, ntype)
dat += "</center>"
else
dat += "<p> No client attached to mob </p>"
dat += "<hr style='background:#000000; border:0; height:1px'>"
dat += "<center><p>Mob</p></center>"
dat += "<center><p>Mob</p></center>"
//Add the links for the mob specific log
dat += "<center><a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_ATTACK_LOG];log_src=[LOGSRC_MOB]'>Attack log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_SAY_LOG];log_src=[LOGSRC_MOB]'>Say log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_EMOTE_LOG];log_src=[LOGSRC_MOB]'>Emote log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_OOC_LOG];log_src=[LOGSRC_MOB]'>OOC log</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[INDIVIDUAL_SHOW_ALL_LOG];log_src=[LOGSRC_MOB]'>Show all</a> | "
dat += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[type];log_src=[LOGSRC_MOB]'>Refresh</a></center>"
dat += "<center>"
dat += individual_logging_panel_link(M, INDIVIDUAL_ATTACK_LOG, LOGSRC_MOB, "Attack Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_SAY_LOG, LOGSRC_MOB, "Say Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_EMOTE_LOG, LOGSRC_MOB, "Emote Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_COMMS_LOG, LOGSRC_MOB, "Comms Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_OOC_LOG, LOGSRC_MOB, "OOC Log", source, ntype)
dat += " | "
dat += individual_logging_panel_link(M, INDIVIDUAL_SHOW_ALL_LOG, LOGSRC_MOB, "Show All", source, ntype)
dat += "</center>"
dat += "<hr style='background:#000000; border:0; height:1px'>"
@@ -31,22 +47,21 @@
if(source == LOGSRC_CLIENT && M.client) //if client doesn't exist just fall back to the mob log
log_source = M.client.player_details.logging //should exist, if it doesn't that's a bug, don't check for it not existing
if(type == INDIVIDUAL_SHOW_ALL_LOG)
dat += "<center>Displaying all [source] logs of [key_name(M)]</center><br><hr>"
for(var/log_type in log_source)
dat += "<center><b>[log_type]</b></center><br>"
for(var/log_type in log_source)
var/nlog_type = text2num(log_type)
if(nlog_type & ntype)
var/list/reversed = log_source[log_type]
if(islist(reversed))
reversed = reverseRange(reversed.Copy())
for(var/entry in reversed)
dat += "<font size=2px>[entry]: [reversed[entry]]</font><br>"
dat += "<font size=2px><b>[entry]</b><br>[reversed[entry]]</font><br>"
dat += "<hr>"
else
dat += "<center>[source] [type] of [key_name(M)]</center><br>"
var/list/reversed = log_source[type]
if(reversed)
reversed = reverseRange(reversed.Copy())
for(var/entry in reversed)
dat += "<font size=2px>[entry]: [reversed[entry]]</font><hr>"
usr << browse(dat, "window=invidual_logging_[key_name(M)];size=600x480")
/proc/individual_logging_panel_link(mob/M, log_type, log_src, label, selected_src, selected_type)
var/slabel = label
if(selected_type == log_type && selected_src == log_src)
slabel = "<b>\[[label]\]</b>"
return "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[log_type];log_src=[log_src]'>[slabel]</a>"
@@ -4,7 +4,7 @@
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates
var/map = input(src, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates
if(!map)
return
template = SSmapping.map_templates[map]
@@ -18,35 +18,50 @@
var/image/item = image('icons/turf/overlays.dmi',S,"greenOverlay")
item.plane = ABOVE_LIGHTING_PLANE
preview += item
usr.client.images += preview
if(alert(usr,"Confirm location.","Template Confirm","Yes","No") == "Yes")
images += preview
if(alert(src,"Confirm location.","Template Confirm","Yes","No") == "Yes")
if(template.load(T, centered = TRUE))
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]</span>")
message_admins("<span class='adminnotice'>[key_name_admin(src)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]</span>")
else
to_chat(usr, "Failed to place map")
usr.client.images -= preview
to_chat(src, "Failed to place map")
images -= preview
/client/proc/map_template_upload()
set category = "Debug"
set name = "Map Template - Upload"
var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
var/map = input(src, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
if(!map)
return
if(copytext("[map]",-4) != ".dmm")
to_chat(usr, "Bad map file: [map]")
to_chat(src, "<span class='warning'>Filename must end in '.dmm': [map]</span>")
return
var/datum/map_template/M
switch(alert(usr, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel"))
switch(alert(src, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel"))
if("Normal")
M = new /datum/map_template(map, "[map]")
M = new /datum/map_template(map, "[map]", TRUE)
if("Shuttle")
M = new /datum/map_template/shuttle(map, "[map]")
M = new /datum/map_template/shuttle(map, "[map]", TRUE)
else
return
if(M.preload_size(map))
to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])")
SSmapping.map_templates[M.name] = M
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has uploaded a map template ([map])</span>")
else
to_chat(usr, "Map template '[map]' failed to load properly")
if(!M.cached_map)
to_chat(src, "<span class='warning'>Map template '[map]' failed to parse properly.</span>")
return
var/datum/map_report/report = M.cached_map.check_for_errors()
var/report_link
if(report)
report.show_to(src)
report_link = " - <a href='?src=[REF(report)];[HrefToken(TRUE)];show=1'>validation report</a>"
to_chat(src, "<span class='warning'>Map template '[map]' <a href='?src=[REF(report)];[HrefToken()];show=1'>failed validation</a>.</span>")
if(report.loadable)
var/response = alert(src, "The map failed validation, would you like to load it anyways?", "Map Errors", "Cancel", "Upload Anyways")
if(response != "Upload Anyways")
return
else
alert(src, "The map failed validation and cannot be loaded.", "Map Errors", "Oh Darn")
return
SSmapping.map_templates[M.name] = M
message_admins("<span class='adminnotice'>[key_name_admin(src)] has uploaded a map template '[map]' ([M.width]x[M.height])[report_link].</span>")
to_chat(src, "<span class='notice'>Map template '[map]' ready to place ([M.width]x[M.height])</span>")
+2 -1
View File
@@ -47,7 +47,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/stop_line_profiling,
/client/proc/show_line_profiling,
/client/proc/create_mapping_job_icons,
/client/proc/debug_z_levels
/client/proc/debug_z_levels,
/client/proc/place_ruin
))
/obj/effect/debugging/mapfix_marker
@@ -629,9 +629,11 @@ GLOBAL_PROTECT(VVpixelmovement)
if (O.vv_edit_var(variable, var_new) == FALSE)
to_chat(src, "Your edit was rejected by the object.")
return
vv_update_display(O, "varedited", VV_MSG_EDITED)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args)
log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]")
var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]"
message_admins(msg)
admin_ticket_log(O, msg)
return TRUE
+1 -1
View File
@@ -351,7 +351,7 @@
"template" = list("desc" = "Template", "callback" = CALLBACK(src, .proc/makeERTTemplateModified), "type" = "datum", "path" = "/datum/ert", "subtypesonly" = TRUE, "value" = ertemplate.type),
"teamsize" = list("desc" = "Team Size", "type" = "number", "value" = ertemplate.teamsize),
"mission" = list("desc" = "Mission", "type" = "string", "value" = ertemplate.mission),
"polldesc" = list("desc" = "Ghost poll description", "string" = "text", "value" = ertemplate.polldesc),
"polldesc" = list("desc" = "Ghost poll description", "type" = "string", "value" = ertemplate.polldesc),
"enforce_human" = list("desc" = "Enforce human authority", "type" = "boolean", "value" = "[(CONFIG_GET(flag/enforce_human_authority) ? "Yes" : "No")]"),
"open_armory" = list("desc" = "Open armory doors", "type" = "boolean", "value" = "[(ertemplate.opendoors ? "Yes" : "No")]"),
)
+5 -4
View File
@@ -31,23 +31,24 @@
cross.icon_state = "tome"
font_color = "red"
prayer_type = "CULTIST PRAYER"
deity = "Nar-Sie"
deity = "Nar'Sie"
else if(isliving(usr))
var/mob/living/L = usr
if(L.has_trait(TRAIT_SPIRITUAL))
cross.icon_state = "holylight"
font_color = "blue"
prayer_type = "SPIRITUAL PRAYER"
var/msg_tmp = msg
msg = "<span class='adminnotice'>[icon2html(cross, GLOB.admins)]<b><font color=[font_color]>[prayer_type][deity ? " (to [deity])" : ""]: </font>[ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]:</b> <span class='linkify'>[msg]</span></span>"
for(var/client/C in GLOB.admins)
if(C.prefs.chat_toggles & CHAT_PRAYER)
to_chat(C, msg)
if(C.prefs.toggles & SOUND_PRAYERS)
if(usr.job == "Chaplain")
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
to_chat(usr, "Your prayers have been received by the gods.")
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//log_admin("HELP: [key_name(src)]: [msg]")
+22 -86
View File
@@ -48,12 +48,12 @@
/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set name = "Headset Message"
admin_headset_message(M)
/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null)
var/mob/living/carbon/human/H = M
if(!check_rights(R_ADMIN))
return
@@ -75,7 +75,7 @@
message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.")
return
log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].")
log_directed_talk(src, H, input, LOG_ADMIN, "reply")
message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"")
to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] <span class='bold'>[input].</span> Message ends.\"")
@@ -596,16 +596,28 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/admin_delete(datum/D)
var/atom/A = D
var/coords = istype(A) ? " at ([A.x], [A.y], [A.z])" : ""
if (alert(src, "Are you sure you want to delete:\n[D]\nat[coords]?", "Confirmation", "Yes", "No") == "Yes")
log_admin("[key_name(usr)] deleted [D][coords]")
message_admins("[key_name_admin(usr)] deleted [D][coords]")
var/coords = ""
var/jmp_coords = ""
if(istype(A))
var/turf/T = get_turf(A)
if(T)
coords = "at [COORD(T)]"
jmp_coords = "at [ADMIN_COORDJMP(T)]"
else
jmp_coords = coords = "in nullspace"
if (alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes")
log_admin("[key_name(usr)] deleted [D] [coords]")
message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
if(isturf(D))
var/turf/T = D
T.ScrapeAway()
else
vv_update_display(D, "deleted", VV_MSG_DELETED)
qdel(D)
if(!QDELETED(D))
vv_update_display(D, "deleted", "")
/client/proc/cmd_admin_list_open_jobs()
set category = "Admin"
@@ -1117,7 +1129,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
return
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
new /obj/item/organ/zombie_infection(H)
new /obj/item/organ/zombie_infection/nodamage(H)
message_admins("[key_name_admin(usr)] added a latent zombie infection to all humans.")
log_admin("[key_name(usr)] added a latent zombie infection to all humans.")
@@ -1134,7 +1146,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(confirm != "Yes")
return
for(var/obj/item/organ/zombie_infection/I in GLOB.zombie_infection_list)
for(var/obj/item/organ/zombie_infection/nodamage/I in GLOB.zombie_infection_list)
qdel(I)
message_admins("[key_name_admin(usr)] cured all zombies.")
@@ -1201,82 +1213,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Tip")
/proc/mass_purrbation()
for(var/M in GLOB.mob_list)
if(ishumanbasic(M))
purrbation_apply(M)
CHECK_TICK
/proc/mass_remove_purrbation()
for(var/M in GLOB.mob_list)
if(ishumanbasic(M))
purrbation_remove(M)
CHECK_TICK
/proc/purrbation_toggle(mob/living/carbon/human/H, silent = FALSE)
if(!ishumanbasic(H))
return
if(!iscatperson(H))
purrbation_apply(H, silent)
. = TRUE
else
purrbation_remove(H, silent)
. = FALSE
/proc/purrbation_apply(mob/living/carbon/human/H, silent = FALSE)
if(!ishuman(H))
return
if(iscatperson(H))
return
var/obj/item/organ/ears/cat/ears = new
var/obj/item/organ/tail/cat/tail = new
ears.Insert(H, drop_if_replaced=FALSE)
tail.Insert(H, drop_if_replaced=FALSE)
if(!silent)
to_chat(H, "Something is nya~t right.")
playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1)
/proc/purrbation_remove(mob/living/carbon/human/H, silent = FALSE)
if(!ishuman(H))
return
if(!iscatperson(H))
return
var/obj/item/organ/ears/cat/ears = H.getorgan(/obj/item/organ/ears/cat)
var/obj/item/organ/tail/cat/tail = H.getorgan(/obj/item/organ/tail/cat)
if(ears)
var/obj/item/organ/ears/NE
if(H.dna.species && H.dna.species.mutantears)
// Roundstart cat ears override H.dna.species.mutantears, reset it here.
H.dna.species.mutantears = initial(H.dna.species.mutantears)
if(H.dna.species.mutantears)
NE = new H.dna.species.mutantears()
if(!NE)
// Go with default ears
NE = new /obj/item/organ/ears()
NE.Insert(H, drop_if_replaced = FALSE)
if(tail)
var/obj/item/organ/tail/NT
if(H.dna.species && H.dna.species.mutanttail)
// Roundstart cat tail overrides H.dna.species.mutanttail, reset it here.
H.dna.species.mutanttail = initial(H.dna.species.mutanttail)
if(H.dna.species.mutanttail)
NT = new H.dna.species.mutanttail()
if(NT)
NT.Insert(H, drop_if_replaced = FALSE)
else
tail.Remove(H)
if(!silent)
to_chat(H, "You are no longer a cat.")
/client/proc/modify_goals()
set category = "Debug"
set name = "Modify goals"
@@ -1310,7 +1246,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
/client/proc/smite(mob/living/carbon/human/target as mob)
set name = "Smite"
set category = "Fun"
if(!check_rights(R_ADMIN))
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING)
@@ -14,7 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
@@ -170,7 +170,7 @@
//////SYNDICATE BORG
/obj/item/antag_spawner/nuke_ops/borg_tele
name = "syndicate cyborg teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field.."
desc = "A single-use teleporter designed to quickly reinforce operatives in the field."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
@@ -190,6 +190,7 @@
/obj/item/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(flag)
return
if(!ScientistCheck(user))
@@ -251,6 +252,7 @@
radio_off(M, user)
/obj/item/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(flag)
return
if(!AbductorCheck(user))
@@ -303,6 +305,7 @@
to_chat(user, "<span class='notice'>You switch the device to [mode==MIND_DEVICE_MESSAGE? "TRANSMISSION": "COMMAND"] MODE</span>")
/obj/item/abductor/mind_device/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(!ScientistCheck(user))
return
@@ -359,7 +362,7 @@
to_chat(L, "<span class='italics'>You hear a voice in your head saying: </span><span class='abductor'>[message]</span>")
to_chat(user, "<span class='notice'>You send the message to your target.</span>")
log_talk(user,"[key_name(user)] sent an abductor mind message to [key_name(L)]: '[message]'", LOGSAY)
log_directed_talk(user, L, message, LOG_SAY, "abductor whisper")
/obj/item/firing_pin/abductor
@@ -512,7 +515,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
var/mob/living/carbon/human/H = L
H.forcesay(GLOB.hit_appends)
add_logs(user, L, "stunned")
log_combat(user, L, "stunned")
/obj/item/abductor_baton/proc/SleepAttack(mob/living/L,mob/living/user)
if(L.incapacitated(TRUE, TRUE))
@@ -526,7 +529,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
"<span class='userdanger'>You suddenly feel very drowsy!</span>")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
L.Sleeping(1200)
add_logs(user, L, "put to sleep")
log_combat(user, L, "put to sleep")
else
if(istype(L.get_item_by_slot(SLOT_HEAD), /obj/item/clothing/head/foilhat))
to_chat(user, "<span class='warning'>The specimen's protective headgear is completely blocking our sleep inducement methods!</span>")
@@ -543,16 +546,16 @@ Congratulations! You are now trained for invasive xenobiology research!"}
return
var/mob/living/carbon/C = L
if(!C.handcuffed)
if(C.get_num_arms() >= 2 || C.get_arm_ignore())
if(C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore())
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
C.visible_message("<span class='danger'>[user] begins restraining [C] with [src]!</span>", \
"<span class='userdanger'>[user] begins shaping an energy field around your hands!</span>")
if(do_mob(user, C, 30) && (C.get_num_arms() >= 2 || C.get_arm_ignore()))
if(do_mob(user, C, 30) && (C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore()))
if(!C.handcuffed)
C.handcuffed = new /obj/item/restraints/handcuffs/energy/used(C)
C.update_handcuffed()
to_chat(user, "<span class='notice'>You restrain [C].</span>")
add_logs(user, C, "handcuffed")
log_combat(user, C, "handcuffed")
else
to_chat(user, "<span class='warning'>You fail to restrain [C].</span>")
else
@@ -213,6 +213,7 @@
mob_size = MOB_SIZE_LARGE
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
hud_type = /datum/hud/blobbernaut
var/independent = FALSE
/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize()
@@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
faction = list(ROLE_BLOB)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
call_life = TRUE
hud_type = /datum/hud/blob_overmind
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 100
@@ -184,7 +185,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
blob_points = CLAMP(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_points)]</font></div>"
/mob/camera/blob/say(message)
/mob/camera/blob/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if (!message)
return
@@ -207,7 +208,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
if (!message)
return
log_talk(src,"[key_name(src)] : [message]",LOGSAY)
src.log_talk(message, LOG_SAY)
var/message_a = say_quote(message, get_spans())
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>)</b> [message_a]</font></span>"
@@ -5,6 +5,7 @@
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_HIJACKER
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
@@ -167,7 +167,7 @@
to_chat(owner.current, "We have reached our capacity for abilities.")
return
if(owner.current.has_trait(TRAIT_FAKEDEATH))//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
if(owner.current.has_trait(TRAIT_DEATHCOMA))//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
to_chat(owner.current, "We lack the energy to evolve new abilities right now.")
return
@@ -65,7 +65,7 @@
if(req_stat < user.stat)
to_chat(user, "<span class='warning'>We are incapacitated.</span>")
return 0
if((user.has_trait(TRAIT_FAKEDEATH)) && (!ignores_fakedeath))
if((user.has_trait(TRAIT_DEATHCOMA)) && (!ignores_fakedeath))
to_chat(user, "<span class='warning'>We are incapacitated.</span>")
return 0
return 1
@@ -68,7 +68,7 @@
//Recent as opposed to all because rounds tend to have a LOT of text.
var/list/recent_speech = list()
var/list/say_log = target.logging[INDIVIDUAL_SAY_LOG]
var/list/say_log = target.logging[LOG_SAY]
if(LAZYLEN(say_log) > LING_ABSORB_RECENT_SPEECH)
recent_speech = say_log.Copy(say_log.len-LING_ABSORB_RECENT_SPEECH+1,0) //0 so len-LING_ARS+1 to end of list
@@ -5,6 +5,7 @@
dna_cost = 0
req_dna = 1
req_stat = DEAD
ignores_fakedeath = TRUE
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
@@ -27,7 +28,7 @@
C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/living/user)
if(user.has_trait(TRAIT_FAKEDEATH, "changeling"))
if(user.has_trait(TRAIT_DEATHCOMA, "changeling"))
to_chat(user, "<span class='warning'>We are already reviving.</span>")
return
if(!user.stat) //Confirmation for living changelings if they want to fake their death
@@ -159,6 +159,7 @@
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = IS_SHARP
var/can_drop = FALSE
var/fake = FALSE
/obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic)
. = ..()
@@ -169,6 +170,7 @@
AddComponent(/datum/component/butchering, 60, 80)
/obj/item/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
. = ..()
if(!proximity)
return
if(istype(target, /obj/structure/table))
@@ -236,6 +238,7 @@
fire_sound = 'sound/effects/splat.ogg'
force = 0
max_charges = 1
fire_delay = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
@@ -252,6 +255,11 @@
/obj/item/gun/magic/tentacle/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "<span class='warning'>The [name] is not ready yet.</span>")
/obj/item/gun/magic/tentacle/process_chamber()
. = ..()
if(charges == 0)
qdel(src)
/obj/item/gun/magic/tentacle/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] coils [src] tightly around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (OXYLOSS)
@@ -300,8 +308,12 @@
/obj/item/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
if(H.get_active_held_item() && !H.get_inactive_held_item())
H.swap_hand()
if(H.get_active_held_item())
return
C.grabbedby(H)
C.grippedby(H) //instant aggro grab
C.grippedby(H, instant = TRUE) //instant aggro grab
/obj/item/projectile/tentacle/proc/tentacle_stab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
@@ -316,7 +328,6 @@
/obj/item/projectile/tentacle/on_hit(atom/target, blocked = FALSE)
var/mob/living/carbon/human/H = firer
H.dropItemToGround(source.gun, TRUE) //Unequip thus delete the tentacle on hit
if(blocked >= 100)
return 0
if(isitem(target))
@@ -332,7 +343,11 @@
if(!L.anchored && !L.throwing)//avoid double hits
if(iscarbon(L))
var/mob/living/carbon/C = L
switch(firer.a_intent)
var/firer_intent = INTENT_HARM
var/mob/M = firer
if(istype(M))
firer_intent = M.a_intent
switch(firer_intent)
if(INTENT_HELP)
C.visible_message("<span class='danger'>[L] is pulled by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2)
@@ -33,7 +33,7 @@
if(!.)
return
if(user.has_trait(CHANGELING_DRAIN) || ((user.stat != DEAD) && !(user.has_trait(TRAIT_FAKEDEATH))))
if(user.has_trait(CHANGELING_DRAIN) || ((user.stat != DEAD) && !(user.has_trait(TRAIT_DEATHCOMA))))
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
return FALSE
@@ -93,7 +93,7 @@
return 1
/obj/effect/proc_holder/changeling/sting/transformation/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "transformation sting", " new identity is [selected_dna.dna.real_name]")
log_combat(user, target, "stung", "transformation sting", " new identity is '[selected_dna.dna.real_name]'")
var/datum/dna/NewDNA = selected_dna.dna
if(ismonkey(target))
to_chat(user, "<span class='notice'>Our genes cry out as we sting [target.name]!</span>")
@@ -119,9 +119,7 @@
/obj/item/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
force = 5 //Basically as strong as a punch
/obj/item/melee/arm_blade/false/afterattack(atom/target, mob/user, proximity)
return
fake = TRUE
/obj/effect/proc_holder/changeling/sting/false_armblade/can_sting(mob/user, mob/target)
if(!..())
@@ -134,7 +132,7 @@
return 1
/obj/effect/proc_holder/changeling/sting/false_armblade/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", object="false armblade sting")
log_combat(user, target, "stung", object="false armblade sting")
var/obj/item/held = target.get_active_held_item()
if(held && !target.dropItemToGround(held))
@@ -176,7 +174,7 @@
return changeling.can_absorb_dna(target)
/obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target)
add_logs(user, target, "stung", "extraction sting")
log_combat(user, target, "stung", "extraction sting")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!(changeling.has_dna(target.dna)))
changeling.add_new_profile(target)
@@ -191,7 +189,7 @@
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "mute sting")
log_combat(user, target, "stung", "mute sting")
target.silent += 30
return TRUE
@@ -204,7 +202,7 @@
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "blind sting")
log_combat(user, target, "stung", "blind sting")
to_chat(target, "<span class='danger'>Your eyes burn horrifically!</span>")
target.become_nearsighted(EYE_DAMAGE)
target.blind_eyes(20)
@@ -220,7 +218,7 @@
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "LSD sting")
log_combat(user, target, "stung", "LSD sting")
addtimer(CALLBACK(src, .proc/hallucination_time, target), rand(100,200))
return TRUE
@@ -237,7 +235,7 @@
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "cryo sting")
log_combat(user, target, "stung", "cryo sting")
if(target.reagents)
target.reagents.add_reagent("frostoil", 30)
return TRUE
@@ -40,7 +40,7 @@
/obj/effect/clockwork/city_of_cogs_rift/attack_hand(atom/movable/AM)
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/CollidedWith(atom/movable/AM)
/obj/effect/clockwork/city_of_cogs_rift/Bumped(atom/movable/AM)
if(!QDELETED(AM))
if(isliving(AM))
var/mob/living/L = AM
@@ -57,7 +57,7 @@
/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L)
//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar-Sian cultists are damaged and knocked down for a longer time
//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar'Sian cultists are damaged and knocked down for a longer time
/obj/effect/clockwork/sigil/transgression
name = "dull sigil"
desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground."
@@ -143,7 +143,7 @@
GLOB.application_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>With the conversion of a new servant the Ark's power grows. Application scriptures are now available.</span>")
if(add_servant_of_ratvar(L))
L.log_message("<font color=#BE8700>Conversion was done with a [sigil_name].</font>", INDIVIDUAL_ATTACK_LOG)
L.log_message("conversion was done with a [sigil_name]", LOG_ATTACK, color="BE8700")
if(iscarbon(L))
var/mob/living/carbon/M = L
M.uncuff()
@@ -21,7 +21,7 @@
name = "Inath-neq, the Resonant Cogwheel"
desc = "A humanoid form blazing with blue fire. It radiates an aura of kindness and caring."
clockwork_desc = "One of Ratvar's four generals. Before her current form, Inath-neq was a powerful warrior priestess commanding the Resonant Cogs, a sect of Ratvarian warriors renowned for \
their prowess. After a lost battle with Nar-Sian cultists, Inath-neq was struck down and stated in her dying breath, \
their prowess. After a lost battle with Nar'Sian cultists, Inath-neq was struck down and stated in her dying breath, \
\"The Resonant Cogs shall not fall silent this day, but will come together to form a wheel that shall never stop turning.\" Ratvar, touched by this, granted Inath-neq an eternal body and \
merged her soul with those of the Cogs slain with her on the battlefield."
icon = 'icons/effects/119x268.dmi'
@@ -15,9 +15,7 @@
/obj/effect/clockwork/servant_blocker/Destroy(force)
if(!force)
return QDEL_HINT_LETMELIVE
var/turf/T = get_turf(src)
. = ..()
T.air_update_turf(TRUE)
return ..()
/obj/effect/clockwork/servant_blocker/CanPass(atom/movable/M, turf/target)
var/list/target_contents = M.GetAllContents() + M
@@ -119,7 +119,7 @@
return
/obj/effect/clockwork/spatial_gateway/CollidedWith(atom/movable/AM)
/obj/effect/clockwork/spatial_gateway/Bumped(atom/movable/AM)
..()
if(!QDELETED(AM))
pass_through_gateway(AM, FALSE)
@@ -46,5 +46,5 @@
return
if(ishuman(owner))
clockwork_say(owner, "[text2ratvar("Servants, hear my words: [input]")]", TRUE)
log_talk(owner,"CLOCK:[key_name(owner)] : [input]",LOGSAY)
owner.log_talk(input, LOG_SAY, tag="clockwork")
titled_hierophant_message(owner, input, span_for_name, span_for_message, title)
@@ -1,7 +1,7 @@
/*
The Ratvarian Language
In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar-Sie's language, but is in fact derived from
aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar-Sian cult).
In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar'Sie's language, but is in fact derived from
aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar'Sian cult).
While the canon states that the language of Ratvar and his servants is incomprehensible to the unenlightened as it is a derivative of the most ancient known language, in reality it is
actually very simple. To translate a plain English sentence to Ratvar's tongue, simply move all of the letters thirteen places ahead, starting from "a" if the end of the alphabet is reached.
@@ -53,7 +53,7 @@
L.handcuffed = new/obj/item/restraints/handcuffs/clockwork(L)
L.update_handcuffed()
to_chat(ranged_ability_user, "<span class='neovgre_small'>You shackle [L].</span>")
add_logs(ranged_ability_user, L, "handcuffed")
log_combat(ranged_ability_user, L, "handcuffed")
else
to_chat(ranged_ability_user, "<span class='warning'>You fail to shackle [L].</span>")
@@ -117,12 +117,12 @@
L.adjustOxyLoss(-oxydamage)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE)
clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!"))
add_logs(ranged_ability_user, L, "healed with Sentinel's Compromise")
log_combat(ranged_ability_user, L, "healed with Sentinel's Compromise")
L.visible_message("<span class='warning'>A blue light washes over [L], [has_holy_water ? "causing [L.p_them()] to briefly glow as it mends" : " mending"] [L.p_their()] bruises and burns!</span>", \
"<span class='heavy_brass'>You feel Inath-neq's power healing your wounds[has_holy_water ? " and purging the darkness within you" : ""], but a deep nausea overcomes you!</span>")
else
clockwork_say(ranged_ability_user, text2ratvar("Purge foul darkness!"))
add_logs(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise")
log_combat(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise")
L.visible_message("<span class='warning'>A blue light washes over [L], causing [L.p_them()] to briefly glow!</span>", \
"<span class='heavy_brass'>You feel Inath-neq's power purging the darkness within you!</span>")
playsound(targetturf, 'sound/magic/staff_healing.ogg', 50, 1)
@@ -153,7 +153,7 @@
var/turf/U = get_turf(target)
to_chat(ranged_ability_user, "<span class='brass'>You release the light of Ratvar!</span>")
clockwork_say(ranged_ability_user, text2ratvar("Purge all untruths and honor Engine!"))
add_logs(ranged_ability_user, U, "fired at with Kindle")
log_combat(ranged_ability_user, U, "fired at with Kindle")
playsound(ranged_ability_user, 'sound/magic/blink.ogg', 50, TRUE, frequency = 0.5)
var/obj/item/projectile/kindle/A = new(T)
A.preparePixelProjectile(target, caller, params)
@@ -265,7 +265,7 @@
"<span class='heavy_brass'>You direct the judicial force to [target].</span>")
var/turf/targetturf = get_turf(target)
new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user)
add_logs(ranged_ability_user, targetturf, "created a judicial marker")
log_combat(ranged_ability_user, targetturf, "created a judicial marker")
remove_ranged_ability()
return TRUE
@@ -5,7 +5,7 @@
clockwork_desc = null
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/component_id //What the component is identified as
var/cultist_message = "You are not worthy of this meme." //Showed to Nar-Sian cultists if they pick up the component in addition to chaplains
var/cultist_message = "You are not worthy of this meme." //Showed to Nar'Sian cultists if they pick up the component in addition to chaplains
var/list/servant_of_ratvar_messages = list("ayy" = FALSE, "lmao" = TRUE) //Fluff, shown to servants of Ratvar on a low chance, if associated value is TRUE, will automatically apply ratvarian
var/message_span = "heavy_brass"
@@ -22,12 +22,12 @@
if(GLOB.ratvar_awakens)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list("melee" = 70, "bullet" = 80, "laser" = -15, "energy" = 25, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list("melee" = 60, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
@@ -83,12 +83,12 @@
if(GLOB.ratvar_awakens)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list("melee" = 70, "bullet" = 80, "laser" = -15, "energy" = 25, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list("melee" = 60, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
@@ -149,7 +149,7 @@
if(GLOB.ratvar_awakens)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list("melee" = 80, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
@@ -313,7 +313,7 @@
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Scripture")
dat += "<font color=#BE8700 size=3>The Ancient Scripture</font><br><br>"
dat += "If you have experience with the Nar-Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \
dat += "If you have experience with the Nar'Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \
of the cult's supernatural ability comes from. The Servant equivalent of runes is called <b>scripture</b>, and unlike runes, scripture is loaded into your clockwork slab.<br><br>"
dat += "Each piece of scripture has widely-varying effects. Your most important scripture, <i>Geis</i>, is obvious and suspicious, but charges your slab with energy and allows \
you to attack a non-Servant in melee range to restrain them and begin converting them into a Servant. This is just one example; each piece of scripture can be simple or \
@@ -136,7 +136,7 @@
ranged_ability_user.visible_message("<span class='warning'>[ranged_ability_user]'s judicial visor fires a stream of energy at [target], creating a strange mark!</span>", "<span class='heavy_brass'>You direct [visor]'s power to [target]. You must wait for some time before doing this again.</span>")
var/turf/targetturf = get_turf(target)
new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user)
add_logs(ranged_ability_user, targetturf, "created a judicial marker")
log_combat(ranged_ability_user, targetturf, "created a judicial marker")
ranged_ability_user.update_action_buttons_icon()
ranged_ability_user.update_inv_glasses()
addtimer(CALLBACK(visor, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown)//Cooldown is reduced by 10x if Ratvar is up
@@ -209,7 +209,7 @@
targetsjudged++
if(!QDELETED(L))
L.adjustBruteLoss(20) //does a decent amount of damage
add_logs(user, L, "struck with a judicial blast")
log_combat(user, L, "struck with a judicial blast")
to_chat(user, "<span class='brass'><b>[targetsjudged ? "Successfully judged <span class='neovgre'>[targetsjudged]</span>":"Judged no"] heretic[targetsjudged == 1 ? "":"s"].</b></span>")
sleep(3) //so the animation completes properly
qdel(src)
@@ -1,4 +1,4 @@
//Wraith spectacles: Grants X-ray and night vision at the eventual cost of the wearer's sight if worn too long. Nar-Sian cultists are instantly blinded.
//Wraith spectacles: Grants X-ray and night vision at the eventual cost of the wearer's sight if worn too long. Nar'Sian cultists are instantly blinded.
/obj/item/clothing/glasses/wraith_spectacles
name = "antique spectacles"
desc = "Unnerving glasses with opaque yellow lenses."
@@ -48,7 +48,7 @@
/obj/item/clothing/glasses/wraith_spectacles/proc/blind_cultist(mob/living/victim)
if(iscultist(victim))
to_chat(victim, "<span class='heavy_brass'>\"It looks like Nar-Sie's dogs really don't value their eyes.\"</span>")
to_chat(victim, "<span class='heavy_brass'>\"It looks like Nar'Sie's dogs really don't value their eyes.\"</span>")
to_chat(victim, "<span class='userdanger'>Your eyes explode with horrific pain!</span>")
victim.emote("scream")
victim.become_blind(EYE_DAMAGE)
@@ -76,7 +76,7 @@
E = new V
E.Grant(src)
/mob/camera/eminence/say(message)
/mob/camera/eminence/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
@@ -86,7 +86,7 @@
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message)
return
log_talk(src, "[key_name(src)] : [message]", LOGSAY)
src.log_talk(message, LOG_SAY, tag="clockwork eminence")
if(GLOB.ratvar_awakens)
visible_message("<span class='brass'><b>You feel light slam into your mind and form words:</b> \"[capitalize(message)]\"</span>")
playsound(src, 'sound/machines/clockcult/ark_scream.ogg', 50, FALSE)
@@ -335,7 +335,7 @@
sleep(3)
GLOB.clockwork_gateway_activated = TRUE
var/turf/T = SSmapping.get_station_center()
new /obj/structure/destructible/clockwork/massive/ratvar(T, TRUE) //Citadel edit - hugboxes ratvar spawning by admins
new /obj/structure/destructible/clockwork/massive/ratvar(T)
SSticker.force_ending = TRUE
var/x0 = T.x
var/y0 = T.y
@@ -71,7 +71,7 @@
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',75 * get_efficiency_mod(),1)
else
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',75 * get_efficiency_mod(),1)
L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar-Sian cultists take additional damage
L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar'Sian cultists take additional damage
if(GLOB.ratvar_awakens && L)
L.adjust_fire_stacks(damage_per_tick)
L.IgniteMob()
@@ -13,11 +13,11 @@
light_range = 15
light_color = "#BE8700"
var/atom/prey //Whatever Ratvar is chasing
var/clashing = FALSE //If Ratvar is fighting with Nar-Sie
var/clashing = FALSE //If Ratvar is fighting with Nar'Sie
var/convert_range = 10
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
/obj/structure/destructible/clockwork/massive/ratvar/Initialize(mapload, is_from_gateway = FALSE)
/obj/structure/destructible/clockwork/massive/ratvar/Initialize()
. = ..()
GLOB.ratvar_awakens++
for(var/obj/O in GLOB.all_clockwork_objects)
@@ -29,8 +29,7 @@
sound_to_playing_players('sound/effects/ratvar_reveal.ogg')
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert")
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [get_area_name(src)] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
if(is_from_gateway) //citadel edit - hugbox
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
GLOB.ratvar_awakens--
@@ -48,7 +47,7 @@
R.visible_message("<span class='heavy_brass'>[R] forms, and its eyes blink open, glowing bright red!</span>")
R.key = O.key
/obj/structure/destructible/clockwork/massive/ratvar/Collide(atom/A)
/obj/structure/destructible/clockwork/massive/ratvar/Bump(atom/A)
var/turf/T = get_turf(A)
if(T == loc)
T = get_step(T, dir) //please don't run into a window like a bird, ratvar
@@ -72,7 +71,7 @@
if(L.z == z && !is_servant_of_ratvar(L) && L.mind)
meals += L
if(GLOB.cult_narsie && GLOB.cult_narsie.z == z)
meals = list(GLOB.cult_narsie) //if you're in the way, handy for him, but ratvar only cares about nar-sie!
meals = list(GLOB.cult_narsie) //if you're in the way, handy for him, but ratvar only cares about Nar'Sie!
prey = GLOB.cult_narsie
if(get_dist(src, prey) <= 10)
clash()
@@ -94,7 +93,7 @@
<span class='userdanger'>You feel tremendous relief as the crushing focus relents...</span>")
prey = null
else
dir_to_step_in = get_dir(src, prey) //Unlike Nar-Sie, Ratvar ruthlessly chases down his target
dir_to_step_in = get_dir(src, prey) //Unlike Nar'Sie, Ratvar ruthlessly chases down his target
step(src, dir_to_step_in)
/obj/structure/destructible/clockwork/massive/ratvar/proc/clash()
@@ -107,7 +106,7 @@
clash_of_the_titans(GLOB.cult_narsie) // >:(
return TRUE
//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar-Sie
//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar'Sie
/obj/structure/destructible/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie)
var/winner = "Undeclared"
var/base_victory_chance = 1
@@ -133,18 +132,18 @@
flash_color(M, flash_color="#C80000", flash_time=1)
shake_camera(M, 4, 3)
if(narsie_chance > ratvar_chance)
winner = "Nar-Sie"
winner = "Nar'Sie"
break
base_victory_chance *= 2 //The clash has a higher chance of resolving each time both gods attack one another
switch(winner)
if("Ratvar")
send_to_playing_players("<span class='heavy_brass'><font size=5>\"[pick("DIE.", "ROT.")]\"</font></span>\n\
<span class='cult'><font size=5>\"<b>[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"</b></font></span>") //nar-sie get out
<span class='cult'><font size=5>\"<b>[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"</b></font></span>") //Nar'Sie get out
sound_to_playing_players('sound/magic/clockwork/anima_fragment_attack.ogg')
sound_to_playing_players('sound/magic/demon_dies.ogg', 50)
clashing = FALSE
qdel(narsie)
if("Nar-Sie")
if("Nar'Sie")
send_to_playing_players("<span class='cult'><font size=5>\"<b>[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]</b>\"</font></span>") //Broken English
sound_to_playing_players('sound/magic/demon_attack1.ogg')
sound_to_playing_players('sound/magic/clockwork/anima_fragment_death.ogg', 62)
@@ -43,11 +43,11 @@
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/CollidedWith(atom/movable/AM)
/obj/structure/destructible/clockwork/taunting_trail/Bumped(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/Collide(atom/movable/AM)
/obj/structure/destructible/clockwork/taunting_trail/Bump(atom/movable/AM)
affect_mob(AM)
return ..()
@@ -53,7 +53,7 @@
SSticker.mode.servants_of_ratvar += owner
SSticker.mode.update_servant_icons_added(owner)
owner.special_role = ROLE_SERVANT_OF_RATVAR
owner.current.log_message("<font color=#BE8700>Has been converted to the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
owner.current.log_message("has been converted to the cult of Ratvar!", LOG_ATTACK, color="#BE8700")
if(issilicon(current))
if(iscyborg(current) && !silent)
var/mob/living/silicon/robot/R = current
@@ -161,7 +161,7 @@
if(!silent)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] seems to have remembered [owner.current.p_their()] true allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.</span>")
owner.current.log_message("<font color=#BE8700>Has renounced the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
owner.current.log_message("has renounced the cult of Ratvar!", LOG_ATTACK, color="#BE8700")
owner.special_role = null
if(iscyborg(owner.current))
to_chat(owner.current, "<span class='warning'>Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.</span>")
+18 -14
View File
@@ -7,8 +7,8 @@
/datum/action/innate/cult/blood_magic/Grant()
..()
button.screen_loc = "6:-29,4:-2"
button.moved = "6:-29,4:-2"
button.screen_loc = DEFAULT_BLOODSPELLS
button.moved = DEFAULT_BLOODSPELLS
button.ordered = FALSE
/datum/action/innate/cult/blood_magic/Remove()
@@ -84,7 +84,7 @@
to_chat(owner, "<span class='warning'>Your wounds glows with power, you have prepared a [new_spell.name] invocation!</span>")
channeling = FALSE
/datum/action/innate/cult/blood_spell //The next generation of talismans
/datum/action/innate/cult/blood_spell //The next generation of talismans, handles storage/creation of blood magic
name = "Blood Magic"
button_icon_state = "telerune"
desc = "Fear the Old Blood."
@@ -161,7 +161,7 @@
/datum/action/innate/cult/blood_spell/emp/Activate()
owner.visible_message("<span class='warning'>[owner]'s hand flashes a bright blue!</span>", \
"<span class='cultitalic'>You speak the cursed words, emitting an EMP blast from your hand.</span>")
empulse(owner, 3, 6)
empulse(owner, 2, 5)
owner.whisper(invocation, language = /datum/language/common)
charges--
if(charges<=0)
@@ -179,11 +179,11 @@
desc = "<u>A sinister spell used to convert:</u><br>Plasteel into runed metal<br>25 metal into a construct shell<br>Cyborgs directly into constructs<br>Cyborg shells into construct shells<br>Airlocks into runed airlocks (harm intent)"
button_icon_state = "transmute"
magic_path = "/obj/item/melee/blood_magic/construction"
health_cost = 10
health_cost = 12
/datum/action/innate/cult/blood_spell/equipment
name = "Summon Equipment"
desc = "A crucial spell that enables you to summon either a ritual dagger or combat gear including armored robes, the nar'sien bola, and an eldritch longsword."
desc = "A crucial spell that enables you to summon either a ritual dagger or combat gear including armored robes, the Nar'Sien bola, and an eldritch longsword."
button_icon_state = "equip"
magic_path = "/obj/item/melee/blood_magic/armor"
@@ -378,11 +378,12 @@
uses = 0
qdel(src)
return
add_logs(user, M, "used a cult spell on", source.name, "")
log_combat(user, M, "used a cult spell on", source.name, "")
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
. = ..()
if(invocation)
user.whisper(invocation, language = /datum/language/common)
if(health_cost)
@@ -400,7 +401,7 @@
//Stun
/obj/item/melee/blood_magic/stun
name = "Stunning Aura "
color = "#ff0000" // red
color = RUNE_COLOR_RED
invocation = "Fuu ma'jin!"
/obj/item/melee/blood_magic/stun/afterattack(atom/target, mob/living/carbon/user, proximity)
@@ -488,7 +489,7 @@
/obj/item/melee/blood_magic/shackles/afterattack(atom/target, mob/living/carbon/user, proximity)
if(iscultist(user) && iscarbon(target) && proximity)
var/mob/living/carbon/C = target
if(C.get_num_arms() >= 2 || C.get_arm_ignore())
if(C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore())
CuffAttack(C, user)
else
user.visible_message("<span class='cultitalic'>This victim doesn't have enough arms to complete the restraint!</span>")
@@ -506,7 +507,7 @@
C.update_handcuffed()
C.silent += 5
to_chat(user, "<span class='notice'>You shackle [C].</span>")
add_logs(user, C, "shackled")
log_combat(user, C, "shackled")
uses--
else
to_chat(user, "<span class='warning'>[C] is already bound.</span>")
@@ -585,10 +586,13 @@
new /obj/structure/constructshell(T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
else if(istype(target,/obj/machinery/door/airlock))
target.narsie_act()
uses--
user.visible_message("<span class='warning'>Black ribbons suddenly eminate from [user]'s hand and cling to the airlock - twisting and corrupting it!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
playsound(T, 'sound/machines/airlockforced.ogg', 50, 1)
do_sparks(5, TRUE, target)
if(do_after(user, 50, target = user))
target.narsie_act()
uses--
user.visible_message("<span class='warning'>Black ribbons suddenly emanate from [user]'s hand and cling to the airlock - twisting and corrupting it!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
else
to_chat(user, "<span class='warning'>The spell will not work on [target]!</span>")
return
+76 -9
View File
@@ -11,9 +11,9 @@
job_rank = ROLE_CULTIST
var/ignore_implant = FALSE
var/give_equipment = FALSE
var/datum/team/cult/cult_team
/datum/antagonist/cult/get_team()
return cult_team
@@ -60,7 +60,7 @@
equip_cultist(TRUE)
SSticker.mode.cult += owner // Only add after they've been given objectives
SSticker.mode.update_cult_icons_added(owner)
current.log_message("<font color=#960000>Has been converted to the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
current.log_message("has been converted to the cult of Nar'Sie!", LOG_ATTACK, color="#960000")
if(cult_team.blood_target && cult_team.blood_target_image && current.client)
current.client.images += cult_team.blood_target_image
@@ -111,7 +111,11 @@
if(ishuman(current))
magic.Grant(current)
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
if(cult_team.cult_risen)
cult_team.rise(current)
if(cult_team.cult_ascendent)
cult_team.ascend(current)
/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
@@ -123,14 +127,19 @@
communion.Remove(current)
magic.Remove(current)
current.clear_alert("bloodsense")
if(ishuman(current))
var/mob/living/carbon/human/H = current
H.eye_color = initial(H.eye_color)
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
H.remove_trait(CULT_EYES)
H.update_body()
/datum/antagonist/cult/on_removal()
SSticker.mode.cult -= owner
SSticker.mode.update_cult_icons_removed(owner)
if(!silent)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!</span>", null, null, null, owner.current)
to_chat(owner.current, "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.</span>")
owner.current.log_message("<font color=#960000>Has renounced the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
owner.current.log_message("has renounced the cult of Nar'Sie!", LOG_ATTACK, color="#960000")
if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
owner.current.client.images -= cult_team.blood_target_image
. = ..()
@@ -193,7 +202,10 @@
throwing.Grant(current)
current.update_action_buttons_icon()
current.apply_status_effect(/datum/status_effect/cult_master)
if(cult_team.cult_risen)
cult_team.rise(current)
if(cult_team.cult_ascendent)
cult_team.ascend(current)
/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
@@ -204,6 +216,14 @@
throwing.Remove(current)
current.update_action_buttons_icon()
current.remove_status_effect(/datum/status_effect/cult_master)
if(ishuman(current))
var/mob/living/carbon/human/H = current
H.eye_color = initial(H.eye_color)
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
H.remove_trait(CULT_EYES)
H.cut_overlays()
H.regenerate_icons()
/datum/team/cult
name = "Cult"
@@ -215,7 +235,53 @@
var/cult_vote_called = FALSE
var/mob/living/cult_master
var/reckoning_complete = FALSE
var/cult_risen = FALSE
var/cult_ascendent = FALSE
/datum/team/cult/proc/check_size()
if(cult_ascendent)
return
var/alive = 0
var/cultplayers = 0
for(var/I in GLOB.player_list)
var/mob/M = I
if(M.stat != DEAD)
if(iscultist(M))
++cultplayers
else
++alive
var/ratio = cultplayers/alive
if(ratio > CULT_RISEN && !cult_risen)
for(var/datum/mind/B in members)
if(B.current)
SEND_SOUND(B.current, 'sound/hallucinations/i_see_you2.ogg')
to_chat(B.current, "<span class='cultlarge'>The veil weakens as your cult grows, your eyes begin to glow...")
addtimer(CALLBACK(src, .proc/rise, B.current), 200)
cult_risen = TRUE
if(ratio > CULT_ASCENDENT && !cult_ascendent)
for(var/datum/mind/B in members)
if(B.current)
SEND_SOUND(B.current, 'sound/hallucinations/im_here1.ogg')
to_chat(B.current, "<span class='cultlarge'>Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!")
addtimer(CALLBACK(src, .proc/ascend, B.current), 200)
cult_ascendent = TRUE
/datum/team/cult/proc/rise(cultist)
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
H.eye_color = "f00"
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
H.add_trait(CULT_EYES)
H.update_body()
/datum/team/cult/proc/ascend(cultist)
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir)
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6")
H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -BODY_FRONT_LAYER))
/datum/team/cult/proc/setup_objectives()
//SAC OBJECTIVE , todo: move this to objective internals
@@ -257,6 +323,7 @@
summon_objective.team = src
objectives += summon_objective
/datum/objective/sacrifice
var/sacced = FALSE
var/sac_image
@@ -285,7 +352,7 @@
update_explanation_text()
/datum/objective/eldergod/update_explanation_text()
explanation_text = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. <b>The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
explanation_text = "Summon Nar'Sie by invoking the rune 'Summon Nar'Sie'. <b>The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
/datum/objective/eldergod/check_completion()
return summoned || completed
@@ -300,7 +367,7 @@
var/list/parts = list()
if(check_cult_victory())
parts += "<span class='greentext big'>The cult has succeeded! Nar-sie has snuffed out another torch in the void!</span>"
parts += "<span class='greentext big'>The cult has succeeded! Nar'Sie has snuffed out another torch in the void!</span>"
else
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
+7 -7
View File
@@ -45,7 +45,7 @@
var/link = FOLLOW_LINK(M, user)
to_chat(M, "[link] [my_message]")
log_talk(user,"CULT:[key_name(user)] : [message]",LOGSAY)
user.log_talk(message, LOG_SAY, tag="cult")
/datum/action/innate/cult/comm/spirit
name = "Spiritual Communion"
@@ -79,7 +79,7 @@
return ..()
/datum/action/innate/cult/mastervote/Activate()
var/choice = alert(owner, "The mantle of leadership is a heavy. Success in this role requires an expert level of communication and experience. Are you sure?",, "Yes", "No")
var/choice = alert(owner, "The mantle of leadership is heavy. Success in this role requires an expert level of communication and experience. Are you sure?",, "Yes", "No")
if(choice == "Yes" && IsAvailable())
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
pollCultists(owner,C.cult_team)
@@ -196,15 +196,15 @@
/datum/action/innate/cult/master/finalreck/proc/chant(chant_number)
switch(chant_number)
if(1)
owner.say("C'arta forbici!", language = /datum/language/common)
owner.say("C'arta forbici!", language = /datum/language/common, forced = "cult invocation")
if(2)
owner.say("Pleggh e'ntrath!", language = /datum/language/common)
owner.say("Pleggh e'ntrath!", language = /datum/language/common, forced = "cult invocation")
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 50, 1)
if(3)
owner.say("Barhah hra zar'garis!", language = /datum/language/common)
owner.say("Barhah hra zar'garis!", language = /datum/language/common, forced = "cult invocation")
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 75, 1)
if(4)
owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common)
owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common, forced = "cult invocation")
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 100, 1)
/datum/action/innate/cult/master/cultmark
@@ -334,7 +334,7 @@
if(cooldown>world.time)
reset_blood_target(C.cult_team)
to_chat(owner, "<span class='cultbold'>You have cleared the cult's blood target!</span>")
qdel(C.cult_team.blood_target_reset_timer)
deltimer(C.cult_team.blood_target_reset_timer)
return
else
to_chat(owner, "<span class='cultbold'>The cult has already designated a target!</span>")
+23 -22
View File
@@ -23,7 +23,7 @@
actions_types = list(/datum/action/item_action/cult_dagger)
/obj/item/melee/cultblade/dagger/Initialize()
..()
. = ..()
var/image/I = image(icon = 'icons/effects/blood.dmi' , icon_state = null, loc = src)
I.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cult_dagger", I)
@@ -81,7 +81,7 @@
/obj/item/twohanded/required/cult_bastard
name = "bloody bastard sword"
desc = "An enormous sword used by Nar-Sien cultists to rapidly harvest the souls of non-believers."
desc = "An enormous sword used by Nar'Sien cultists to rapidly harvest the souls of non-believers."
w_class = WEIGHT_CLASS_HUGE
block_chance = 50
throwforce = 20
@@ -251,8 +251,8 @@
holder.update_action_buttons_icon()
/obj/item/restraints/legcuffs/bola/cult
name = "nar'sien bola"
desc = "A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'sien cultists. Throw it to trip and slow your victim."
name = "\improper Nar'Sien bola"
desc = "A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'Sien cultists. Throw it to trip and slow your victim."
icon_state = "bola_cult"
breakouttime = 60
knockdown = 20
@@ -297,7 +297,7 @@
/obj/item/clothing/head/culthood/alt
name = "cultist hood"
desc = "An armored hood worn by the followers of Nar-Sie."
desc = "An armored hood worn by the followers of Nar'Sie."
icon_state = "cult_hoodalt"
item_state = "cult_hoodalt"
@@ -306,7 +306,7 @@
/obj/item/clothing/suit/cultrobes/alt
name = "cultist robes"
desc = "An armored set of robes worn by the followers of Nar-Sie."
desc = "An armored set of robes worn by the followers of Nar'Sie."
icon_state = "cultrobesalt"
item_state = "cultrobesalt"
@@ -318,14 +318,14 @@
name = "magus helm"
icon_state = "magus"
item_state = "magus"
desc = "A helm worn by the followers of Nar-Sie."
desc = "A helm worn by the followers of Nar'Sie."
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDEEARS|HIDEEYES
armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 10, "acid" = 10)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/obj/item/clothing/suit/magusred
name = "magus robes"
desc = "A set of armored robes worn by the followers of Nar-Sie."
desc = "A set of armored robes worn by the followers of Nar'Sie."
icon_state = "magusred"
item_state = "magusred"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
@@ -334,8 +334,8 @@
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
/obj/item/clothing/head/helmet/space/hardsuit/cult
name = "\improper Nar-Sien hardened helmet"
desc = "A heavily-armored helmet worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
name = "\improper Nar'Sien hardened helmet"
desc = "A heavily-armored helmet worn by warriors of the Nar'Sien cult. It can withstand hard vacuum."
icon_state = "cult_helmet"
item_state = "cult_helmet"
armor = list("melee" = 60, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75)
@@ -343,10 +343,10 @@
actions_types = list()
/obj/item/clothing/suit/space/hardsuit/cult
name = "\improper Nar-Sien hardened armor"
name = "\improper Nar'Sien hardened armor"
icon_state = "cult_armor"
item_state = "cult_armor"
desc = "A heavily-armored exosuit worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
desc = "A heavily-armored exosuit worn by warriors of the Nar'Sien cult. It can withstand hard vacuum."
w_class = WEIGHT_CLASS_BULKY
allowed = list(/obj/item/tome, /obj/item/melee/cultblade, /obj/item/tank/internals/)
armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75)
@@ -456,13 +456,13 @@
user.dropItemToGround(src, TRUE)
/obj/item/clothing/glasses/hud/health/night/cultblind
desc = "may Nar-Sie guide you through the darkness and shield you from the light."
desc = "may Nar'Sie guide you through the darkness and shield you from the light."
name = "zealot's blindfold"
icon_state = "blindfold"
item_state = "blindfold"
flash_protect = 1
/obj/item/clothing/glasses/night/cultblind/equipped(mob/living/user, slot)
/obj/item/clothing/glasses/hud/health/night/cultblind/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
to_chat(user, "<span class='cultlarge'>\"You want to be blind, do you?\"</span>")
@@ -496,7 +496,7 @@
to_chat(user, "<span class='notice'>We have exhausted our ability to curse the shuttle.</span>")
return
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
to_chat(user, "<span class='warning'>Nar-Sie is already on this plane, there is no delaying the end of all things.</span>")
to_chat(user, "<span class='warning'>Nar'Sie is already on this plane, there is no delaying the end of all things.</span>")
return
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
@@ -589,7 +589,7 @@
/obj/item/flashlight/flare/culttorch
name = "void torch"
desc = "Used by veteran cultists to instantly transport items to their needful bretheren."
desc = "Used by veteran cultists to instantly transport items to their needful brethren."
w_class = WEIGHT_CLASS_SMALL
brightness_on = 1
icon_state = "torch"
@@ -770,7 +770,7 @@
damage_type = BRUTE
impact_effect_type = /obj/effect/temp_visual/dir_setting/bloodsplatter
/obj/item/projectile/magic/arcane_barrage/blood/Collide(atom/target)
/obj/item/projectile/magic/arcane_barrage/blood/Bump(atom/target)
var/turf/T = get_turf(target)
playsound(T, 'sound/effects/splat.ogg', 50, TRUE)
if(iscultist(target))
@@ -804,6 +804,7 @@
/obj/item/blood_beam/afterattack(atom/A, mob/living/user, flag, params)
. = ..()
if(firing || charging)
return
var/C = user.client
@@ -897,7 +898,7 @@
/obj/item/shield/mirror
name = "mirror shield"
desc = "An infamous shield used by Nar'sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy."
desc = "An infamous shield used by Nar'Sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "mirror_shield" // eshield1 for expanded
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
@@ -905,22 +906,22 @@
force = 5
throwforce = 15
throw_speed = 1
throw_range = 6
throw_range = 4
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("bumped", "prodded")
hitsound = 'sound/weapons/smash.ogg'
var/illusions = 3
var/illusions = 2
/obj/item/shield/mirror/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(iscultist(owner))
if(istype(hitby, /obj/item/projectile))
var/obj/item/projectile/P = hitby
if(P.damage >= 35)
if(P.damage >= 30)
var/turf/T = get_turf(owner)
T.visible_message("<span class='warning'>The sheer force from [P] shatters the mirror shield!</span>")
new /obj/effect/temp_visual/cult/sparks(T)
playsound(T, 'sound/effects/glassbr3.ogg', 100)
owner.Knockdown(20)
owner.Knockdown(25)
qdel(src)
return FALSE
if(P.is_reflectable)
@@ -76,7 +76,7 @@
/obj/structure/destructible/cult/talisman
name = "altar"
desc = "A bloodstained altar dedicated to Nar-Sie."
desc = "A bloodstained altar dedicated to Nar'Sie."
icon_state = "talismanaltar"
break_message = "<span class='warning'>The altar shatters, leaving only the wailing of the damned!</span>"
@@ -110,7 +110,7 @@
/obj/structure/destructible/cult/forge
name = "daemon forge"
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie."
desc = "A forge used in crafting the unholy weapons used by the armies of Nar'Sie."
icon_state = "forge"
light_range = 2
light_color = LIGHT_COLOR_LAVA
@@ -131,7 +131,7 @@
return
var/choice
if(user.mind.has_antag_datum(/datum/antagonist/cult/master))
choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword")
choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield")
else
choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield")
var/list/pickedtype = list()
@@ -140,14 +140,6 @@
pickedtype += /obj/item/clothing/suit/hooded/cultrobes/cult_shield
if("Flagellant's Robe")
pickedtype += /obj/item/clothing/suit/hooded/cultrobes/berserker
if("Bastard Sword")
if((world.time - SSticker.round_start_time) >= 12000)
pickedtype += /obj/item/twohanded/required/cult_bastard
else
cooldowntime = 12000 - (world.time - SSticker.round_start_time)
to_chat(user, "<span class='cult italic'>The forge fires are not yet hot enough for this weapon, give it another [DisplayTimeText(cooldowntime)].</span>")
cooldowntime = 0
return
if("Mirror Shield")
pickedtype += /obj/item/shield/mirror
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
@@ -221,7 +213,10 @@
var/turf/T = safepick(validturfs)
if(T)
T.ChangeTurf(/turf/open/floor/engine/cult)
if(istype(T, /turf/open/floor/plating))
T.PlaceOnTop(/turf/open/floor/engine/cult)
else
T.ChangeTurf(/turf/open/floor/engine/cult)
else
var/turf/open/floor/engine/cult/F = safepick(cultturfs)
if(F)
+5 -5
View File
@@ -17,7 +17,7 @@ This file contains the cult dagger and rune list code
/obj/item/melee/cultblade/dagger/examine(mob/user)
..()
if(iscultist(user) || isobserver(user))
to_chat(user, "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie.</span>")
to_chat(user, "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar'Sie.</span>")
to_chat(user, "<span class='cult'>Striking a cult structure will unanchor or reanchor it.</span>")
to_chat(user, "<span class='cult'>Striking another cultist with it will purge holy water from them.</span>")
to_chat(user, "<span class='cult'>Striking a noncultist, however, will tear their flesh.</span>")
@@ -29,7 +29,7 @@ This file contains the cult dagger and rune list code
var/holy2unholy = M.reagents.get_reagent_amount("holywater")
M.reagents.del_reagent("holywater")
M.reagents.add_reagent("unholywater",holy2unholy)
add_logs(user, M, "smacked", src, " removing the holy water from them")
log_combat(user, M, "smacked", src, " removing the holy water from them")
return FALSE
. = ..()
@@ -78,7 +78,7 @@ This file contains the cult dagger and rune list code
return
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
if(!(A in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site (where Nar-sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site (where Nar'Sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
return
if(summon_objective.summon_spots.len < 2)
to_chat(user, "<span class='cultlarge'>Only one ritual site remains - it must be reserved for the final summoning!</span>")
@@ -87,7 +87,7 @@ This file contains the cult dagger and rune list code
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives
if(!summon_objective)
to_chat(user, "<span class='warning'>Nar-Sie does not wish to be summoned!</span>")
to_chat(user, "<span class='warning'>Nar'Sie does not wish to be summoned!</span>")
return
if(sac_objective && !sac_objective.check_completion())
to_chat(user, "<span class='warning'>The sacrifice is not complete. The portal would lack the power to open if you tried!</span>")
@@ -98,7 +98,7 @@ This file contains the cult dagger and rune list code
if(!(A in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!</span>")
return
var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No")
var/confirm_final = alert(user, "This is the FINAL step to summon Nar'Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar'Sie!", "No")
if(confirm_final == "No")
to_chat(user, "<span class='cult'>You decide to prepare further before scribing the rune.</span>")
return
+18 -18
View File
@@ -62,7 +62,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
to_chat(user, "<span class='notice'>You carefully erase the [lowertext(cultist_name)] rune.</span>")
qdel(src)
else if(istype(I, /obj/item/nullrod))
user.say("BEGONE FOUL MAGIKS!!")
user.say("BEGONE FOUL MAGIKS!!", forced = "nullrod")
to_chat(user, "<span class='danger'>You disrupt the magic of [src] with [I].</span>")
qdel(src)
@@ -136,7 +136,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(isliving(M))
var/mob/living/L = M
if(invocation)
L.say(invocation, language = /datum/language/common, ignore_spam = TRUE)
L.say(invocation, language = /datum/language/common, ignore_spam = TRUE, forced = "cult invocation")
if(invoke_damage)
L.apply_damage(invoke_damage, BRUTE)
to_chat(L, "<span class='cult italic'>[src] saps your strength!</span>")
@@ -179,7 +179,7 @@ structure_check() searches for nearby cultist structures required for the invoca
//Rite of Offering: Converts or sacrifices a target.
/obj/effect/rune/convert
cultist_name = "Offer"
cultist_desc = "offers a noncultist above it to Nar-Sie, either converting them or sacrificing them."
cultist_desc = "offers a noncultist above it to Nar'Sie, either converting them or sacrificing them."
req_cultists_text = "2 for conversion, 3 for living sacrifices and sacrifice targets."
invocation = "Mah'weyh pleggh at e'ntrath!"
icon_state = "3"
@@ -211,7 +211,7 @@ structure_check() searches for nearby cultist structures required for the invoca
var/mob/living/F = invokers[1]
var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
var/datum/team/cult/Cult_team = C.cult_team
var/is_convertable = is_convertable_to_cult(L,C.cult_team)
if(L.stat != DEAD && (is_clock || is_convertable))
invocation = "Mah'weyh pleggh at e'ntrath!"
@@ -229,8 +229,8 @@ structure_check() searches for nearby cultist structures required for the invoca
do_sacrifice(L, invokers)
animate(src, color = oldcolor, time = 5)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 5)
Cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew
rune_in_use = FALSE
/obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers)
if(invokers.len < 2)
for(var/M in invokers)
@@ -315,6 +315,7 @@ structure_check() searches for nearby cultist structures required for the invoca
return TRUE
/obj/effect/rune/empower
cultist_name = "Empower"
cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
@@ -439,9 +440,9 @@ structure_check() searches for nearby cultist structures required for the invoca
light_range = 0
update_light()
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
//Ritual of Dimensional Rending: Calls forth the avatar of Nar'Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Nar-Sie"
cultist_name = "Nar'Sie"
cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers."
invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
req_cultists = 9
@@ -479,8 +480,8 @@ structure_check() searches for nearby cultist structures required for the invoca
return
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
for(var/M in invokers)
to_chat(M, "<span class='warning'>Nar-Sie is already on this plane!</span>")
log_game("Nar-Sie rune failed - already summoned")
to_chat(M, "<span class='warning'>Nar'Sie is already on this plane!</span>")
log_game("Nar'Sie rune failed - already summoned")
return
//BEGIN THE SUMMONING
used = TRUE
@@ -490,7 +491,7 @@ structure_check() searches for nearby cultist structures required for the invoca
sleep(40)
if(src)
color = RUNE_COLOR_RED
new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn even if the rune has been removed
new /obj/singularity/narsie/large/cult(T) //Causes Nar'Sie to spawn even if the rune has been removed
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user)))
@@ -512,7 +513,7 @@ structure_check() searches for nearby cultist structures required for the invoca
invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
icon_state = "1"
color = RUNE_COLOR_MEDIUMRED
var/static/revives_used = 0
var/static/revives_used = -SOULS_TO_REVIVE // Cultists get one "free" revive
/obj/effect/rune/raise_dead/examine(mob/user)
..()
@@ -549,11 +550,12 @@ structure_check() searches for nearby cultist structures required for the invoca
invocation = initial(invocation)
..()
if(mob_to_revive.stat == DEAD)
if(LAZYLEN(GLOB.sacrificed) <= revives_used)
to_chat(user, "<span class='warning'>Your cult must carry out another sacrifice before it can revive a cultist!</span>")
var/diff = LAZYLEN(GLOB.sacrificed) - revives_used - SOULS_TO_REVIVE
if(diff < 0)
to_chat(user, "<span class='warning'>Your cult must carry out [abs(diff)] more sacrifice\s before it can revive another cultist!</span>")
fail_invoke()
return
revives_used++
revives_used += SOULS_TO_REVIVE
mob_to_revive.revive(1, 1) //This does remove traits and such, but the rune might actually see some use because of it!
mob_to_revive.grab_ghost()
if(!mob_to_revive.client || mob_to_revive.client.is_afk())
@@ -618,9 +620,7 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(user, "<span class='cultitalic'>The air above this rune has hardened into a barrier that will last [DisplayTimeText(TMR.timeToRun - world.time)].</span>")
/obj/effect/rune/wall/Destroy()
density = FALSE
GLOB.wall_runes -= src
air_update_turf(1)
return ..()
/obj/effect/rune/wall/BlockSuperconductivity()
@@ -863,7 +863,7 @@ structure_check() searches for nearby cultist structures required for the invoca
var/obj/structure/emergency_shield/invoker/N = new(T)
new_human.key = ghost_to_spawn.key
SSticker.mode.add_cultist(new_human.mind, 0)
to_chat(new_human, "<span class='cultitalic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.</b></span>")
to_chat(new_human, "<span class='cultitalic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar'Sie, and you are to serve them at all costs.</b></span>")
while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T))
if(user.stat || new_human.InCritical())
@@ -946,7 +946,7 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(user, "<span class='cultlarge'>Only one ritual site remains - it must be reserved for the final summoning!</span>")
return
if(!(place in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site, where Nar-sie can be summoned, it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site, where Nar'Sie can be summoned, it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
return
summon_objective.summon_spots -= place
rune_in_use = TRUE
@@ -21,6 +21,7 @@
held_items = list(null, null)
bodyparts = list(/obj/item/bodypart/chest/devil, /obj/item/bodypart/head/devil, /obj/item/bodypart/l_arm/devil,
/obj/item/bodypart/r_arm/devil, /obj/item/bodypart/r_leg/devil, /obj/item/bodypart/l_leg/devil)
hud_type = /datum/hud/devil
var/ascended = FALSE
var/mob/living/oldform
var/list/devil_overlays[DEVIL_TOTAL_LAYERS]
@@ -172,14 +173,14 @@
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
"<span class='userdanger'>[M] has punched [src]!</span>")
adjustBruteLoss(damage)
add_logs(M, src, "attacked")
log_combat(M, src, "attacked")
updatehealth()
if ("disarm")
if (!lying && !ascended) //No stealing the arch devil's pitchfork.
if (prob(5))
Unconscious(40)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
add_logs(M, src, "pushed")
log_combat(M, src, "pushed")
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
"<span class='userdanger'>[M] has pushed down [src]!</span>")
else
@@ -27,7 +27,7 @@
/datum/disease/advance/sentient_disease/IsSame(datum/disease/D)
if(istype(src, D.type))
if(istype(D, /datum/disease/advance/sentient_disease))
var/datum/disease/advance/sentient_disease/V = D
if(V.overmind == overmind)
return TRUE
@@ -107,7 +107,7 @@ the new instance inside the host to be updated to the template's stats.
if(istype(B))
to_chat(user, "<span class='notice'>[B.name]</span>")
/mob/camera/disease/say(message)
/mob/camera/disease/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
return
/mob/camera/disease/Move(NewLoc, Dir = 0)
@@ -245,7 +245,7 @@ the new instance inside the host to be updated to the template's stats.
/mob/camera/disease/proc/set_following(mob/living/L)
following_host = L
if(!move_listener)
move_listener = L.AddComponent(/datum/component/redirect, COMSIG_MOVABLE_MOVED, CALLBACK(src, .proc/follow_mob))
move_listener = L.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/follow_mob)))
else
L.TakeComponent(move_listener)
if(QDELING(move_listener))
@@ -261,7 +261,7 @@ the new instance inside the host to be updated to the template's stats.
index = index == hosts.len ? 1 : index + 1
set_following(hosts[index])
/mob/camera/disease/proc/follow_mob(newloc, dir)
/mob/camera/disease/proc/follow_mob(datum/source, newloc, dir)
var/turf/T = get_turf(following_host)
if(T)
forceMove(T)
+1
View File
@@ -12,6 +12,7 @@
var/list/name_source
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_PREVENT
/datum/antagonist/ert/on_gain()
update_name()
@@ -3,6 +3,7 @@
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
+7
View File
@@ -8,6 +8,11 @@
var/give_objectives = TRUE
var/give_equipment = TRUE
/datum/antagonist/ninja/New()
if(helping_station)
can_hijack = HIJACK_PREVENT
. = ..()
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
@@ -137,6 +142,8 @@
adj = "objectiveless"
else
return
if(helping_station)
can_hijack = HIJACK_PREVENT
new_owner.assigned_role = ROLE_NINJA
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
@@ -28,8 +28,8 @@
var/deconstruction_state = NUKESTATE_INTACT
var/lights = ""
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
var/static/bomb_set
/obj/machinery/nuclearbomb/Initialize()
. = ..()
@@ -227,7 +227,6 @@
/obj/machinery/nuclearbomb/process()
if(timing && !exploding)
bomb_set = TRUE
if(detonation_timer < world.time)
explode()
else
@@ -358,26 +357,23 @@
S.switch_mode_to(initial(S.mode))
S.alert = FALSE
timing = FALSE
bomb_set = TRUE
detonation_timer = null
countdown.stop()
update_icon()
/obj/machinery/nuclearbomb/proc/set_active()
if(safety && !bomb_set)
if(safety)
to_chat(usr, "<span class='danger'>The safety is still on.</span>")
return
timing = !timing
if(timing)
previous_level = get_security_level()
bomb_set = TRUE
detonation_timer = world.time + (timer_set * 10)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
else
bomb_set = FALSE
detonation_timer = null
set_security_level(previous_level)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
@@ -460,6 +456,7 @@
/obj/machinery/nuclearbomb/beer
name = "Nanotrasen-brand nuclear fission explosive"
desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back."
proper_bomb = FALSE
var/obj/structure/reagent_dispensers/beerkeg/keg
/obj/machinery/nuclearbomb/beer/Initialize()
@@ -498,7 +495,6 @@
addtimer(CALLBACK(src, .proc/fizzbuzz), 110)
/obj/machinery/nuclearbomb/beer/proc/disarm()
bomb_set = FALSE
detonation_timer = null
exploding = FALSE
exploded = TRUE
+19 -13
View File
@@ -8,6 +8,7 @@
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
var/nukeop_outfit = /datum/outfit/syndicate
can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station.
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
@@ -32,14 +33,9 @@
return
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs
H.equipOutfit(nukeop_outfit)
if(!isplasmaman(owner.current))
return TRUE
var/mob/living/carbon/human/plasma = owner.current
plasma.set_species(/datum/species/human) //Plasmamen burn up otherwise.
return TRUE
/datum/antagonist/nukeop/greet()
@@ -250,8 +246,18 @@
/datum/team/nuclear/proc/disk_rescued()
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
if(!D.onCentCom())
return FALSE
//If emergency shuttle is in transit disk is only safe on it
if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
if(!SSshuttle.emergency.is_in_shuttle_bounds(D))
return FALSE
//If shuttle escaped check if it's on centcom side
else if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME)
if(!D.onCentCom())
return FALSE
else //Otherwise disk is safe when on station
var/turf/T = get_turf(D)
if(!T || !is_station_level(T.z))
return FALSE
return TRUE
/datum/team/nuclear/proc/operatives_dead()
@@ -267,7 +273,7 @@
return S && (is_centcom_level(S.z) || T)
/datum/team/nuclear/proc/get_result()
var/evacuation = SSshuttle.emergency.mode == SHUTTLE_ENDGAME
var/evacuation = EMERGENCY_ESCAPED_OR_ENDGAMED
var/disk_rescued = disk_rescued()
var/syndies_didnt_escape = !syndies_escaped()
var/station_was_nuked = SSticker.mode.station_was_nuked
@@ -275,9 +281,9 @@
if(nuke_off_station == NUKE_SYNDICATE_BASE)
return NUKE_RESULT_FLUKE
else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
else if(station_was_nuked && !syndies_didnt_escape)
return NUKE_RESULT_NUKE_WIN
else if (!disk_rescued && station_was_nuked && syndies_didnt_escape)
else if (station_was_nuked && syndies_didnt_escape)
return NUKE_RESULT_NOSURVIVORS
else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
return NUKE_RESULT_WRONG_STATION
@@ -289,7 +295,7 @@
return NUKE_RESULT_CREW_WIN
else if (!disk_rescued && operatives_dead())
return NUKE_RESULT_DISK_LOST
else if (!disk_rescued && evacuation)
else if (!disk_rescued && evacuation)
return NUKE_RESULT_DISK_STOLEN
else
return //Undefined result
@@ -4,6 +4,7 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
can_hijack = HIJACK_PREVENT
/datum/antagonist/official/greet()
to_chat(owner, "<B><font size=3 color=red>You are a CentCom Official.</font></B>")
@@ -0,0 +1,157 @@
#define INITIAL_CRYSTALS 5 // initial telecrystals in the boss' uplink
// Syndicate mutineer agents. They're agents selected by the Syndicate to take control of stations when assault teams like nuclear operatives cannot be sent.
// They sent teams made of 3 agents, of which only one is woke up at round start. The others are, lore-wise, sleeping agents and must be implanted with the converter to wake up.
// Mechanics wise, it's just 1 dude per team and he can convert maximum 2 more people of his choice, based on the implanter use var, Upon converting, the newly made guys are given access
// to a storage implant they came with when the Syndicate sent them aboard, with one random low-cost traitor item. The initial agent also has this. The only difference between
// initial agents and converted ones is that the initial agent has the items required to convert people and the AI.
/datum/antagonist/overthrow
name = "Syndicate mutineer"
roundend_category = "syndicate mutineers"
antagpanel_category = "Syndicate Mutineers"
job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings
var/datum/team/overthrow/team
var/static/list/possible_useful_items
// Overthrow agent. The idea is based on sleeping agents being sent as crewmembers, with one for each team that starts woken up who can also wake up others with their converter implant.
// Obviously they can just convert anyone, the idea of sleeping agents is just lore. This also explains why this antag type has no deconversion way: they're traitors. Traitors cannot be
// deconverted.
// Generates the list of possible items for the storage implant given on_gain
/datum/antagonist/overthrow/New()
..()
if(!possible_useful_items)
possible_useful_items = list(/obj/item/gun/ballistic/automatic/pistol, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/pen/edagger, /obj/item/pen/sleepy, \
/obj/item/soap/syndie, /obj/item/card/id/syndicate, /obj/item/storage/box/syndie_kit/chameleon)
// Sets objectives, equips all antags with the storage implant.
/datum/antagonist/overthrow/on_gain()
objectives += team.objectives
owner.objectives += objectives
..()
owner.announce_objectives()
equip_overthrow()
owner.special_role = ROLE_OVERTHROW
/datum/antagonist/overthrow/on_removal()
owner.special_role = null
owner.objectives -= objectives
..()
// Creates the overthrow team, or sets it. The objectives are static for all the team members.
/datum/antagonist/overthrow/create_team(datum/team/overthrowers)
if(!overthrowers)
team = new()
team.add_member(owner)
name_team()
team.create_objectives()
else
team = overthrowers
team.add_member(owner)
// Used to name the team at round start. If no name is passed, a syndicate themed one is given randomly.
/datum/antagonist/overthrow/proc/name_team()
var/team_name = stripped_input(owner.current, "Name your team:", "Team name", , MAX_NAME_LEN)
var/already_taken = FALSE
for(var/datum/antagonist/overthrow/O in GLOB.antagonists)
if(team_name == O.name)
already_taken = TRUE
break
if(!team_name || already_taken) // basic protection against two teams with the same name. This could still happen with extreme unluck due to syndicate_name() but it shouldn't break anything.
team.name = syndicate_name()
to_chat(owner, "<span class='danger'>Since you gave [already_taken ? "an already used" : "no"] name, your team's name has been randomly generated: [team.name]!</span>")
return
team.name = team_name
// CLOWNMUT removal and HUD creation/being given
/datum/antagonist/overthrow/apply_innate_effects()
..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
if(!silent)
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
traitor_mob.dna.remove_mutation(CLOWNMUT)
update_overthrow_icons_added()
// The opposite
/datum/antagonist/overthrow/remove_innate_effects()
update_overthrow_icons_removed()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
traitor_mob.dna.add_mutation(CLOWNMUT)
..()
/datum/antagonist/overthrow/get_admin_commands()
. = ..()
.["Give storage with random item"] = CALLBACK(src,.proc/equip_overthrow)
.["Give overthrow boss equip"] = CALLBACK(src,.proc/equip_initial_overthrow_agent)
// Dynamically creates the HUD for the team if it doesn't exist already, inserting it into the global huds list, and assigns it to the user. The index is saved into a var owned by the team datum.
/datum/antagonist/overthrow/proc/update_overthrow_icons_added(datum/mind/traitor_mind)
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num]
if(!overthrowhud)
overthrowhud = new()
team.hud_entry_num = GLOB.huds.len + 1 // the index of the hud inside huds list
GLOB.huds += overthrowhud
overthrowhud.join_hud(owner.current)
set_antag_hud(owner.current, "traitor")
// Removes hud. Destroying the hud datum itself in case the team is deleted is done on team Destroy().
/datum/antagonist/overthrow/proc/update_overthrow_icons_removed(datum/mind/traitor_mind)
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num]
if(overthrowhud)
overthrowhud.leave_hud(owner.current)
set_antag_hud(owner.current, null)
// Gives the storage implant with a random item. They're sleeping agents, after all.
/datum/antagonist/overthrow/proc/equip_overthrow()
if(!owner || !owner.current || !ishuman(owner.current)) // only equip existing human overthrow members. This excludes the AI, in particular.
return
var/obj/item/implant/storage/S = locate(/obj/item/implant/storage) in owner.current
if(!S)
S = new(owner.current)
S.implant(owner.current)
var/I = pick(possible_useful_items)
if(ispath(I)) // in case some admin decides to fuck the list up for fun
I = new I()
SEND_SIGNAL(S, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)
// Equip the initial overthrow agent. Manually called in overthrow gamemode, when the initial agents are chosen. Gives uplink, AI module board and the converter.
/datum/antagonist/overthrow/proc/equip_initial_overthrow_agent()
if(!owner || !owner.current || !ishuman(owner.current))
return
var/mob/living/carbon/human/H = owner.current
// Give uplink
var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src)
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
uplink.telecrystals = INITIAL_CRYSTALS
// Give AI hacking board
var/obj/item/aiModule/core/full/overthrow/O = new(H)
var/list/slots = list (
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/where = H.equip_in_one_of_slots(O, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you the AI module.")
else
to_chat(H, "Use the AI board in your [where] to take control of the AI, as requested by the Syndicate.")
// Give the implant converter
var/obj/item/overthrow_converter/I = new(H)
where = H.equip_in_one_of_slots(I, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you a converter implant.")
else
to_chat(H, "Use the implanter in your [where] to wake up sleeping syndicate agents, so that they can aid you.")
/datum/antagonist/overthrow/get_team()
return team
/datum/antagonist/overthrow/greet()
to_chat(owner.current, "<B><font size=3 color=red>You are a syndicate sleeping agent!</font> <font size=2 color=red>Your job is to stage a swift, fairly bloodless coup. Your team has a two-use converter that can be used to convert \
anyone you want, although mind shield implants need to be removed firstly for it to work. Your team also has a special version of the Syndicate module to be used to convert the AI, too. You \
will be able to use the special storage implant you came aboard with, which contains a random, cheap item from our special selection which will aid in your mission. \
Your team objective is to deal with the heads, the AI and a special target who angered us for several reasons which you're not entitled to know. Converting to your team will let us \
take control of the station faster, so it should be prioritized, especially over killing, which should be avoided where possible. The other Syndicate teams are NOT friends and should not \
be trusted.</font></B>")
@@ -0,0 +1,56 @@
/obj/item/overthrow_converter // nearly equal to an implanter, as an object
name = "agent activation implant"
desc = "Wakes up syndicate sleeping agents."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "implanter1"
item_state = "syringe_0"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
materials = list(MAT_METAL=600, MAT_GLASS=200)
var/uses = 2
/obj/item/overthrow_converter/proc/convert(mob/living/carbon/human/target, mob/living/carbon/human/user) // Should probably also delete any mindshield implant. Not sure.
if(istype(target) && target.mind && user && user.mind)
var/datum/mind/target_mind = target.mind
var/datum/mind/user_mind = user.mind
var/datum/antagonist/overthrow/TO = target_mind.has_antag_datum(/datum/antagonist/overthrow)
var/datum/antagonist/overthrow/UO = user_mind.has_antag_datum(/datum/antagonist/overthrow)
if(!UO)
to_chat(user, "<span class='danger'>You don't know how to use this thing!</span>") // It needs a valid team to work, if you aren't an antag don't use this thing
return FALSE
if(TO)
to_chat(user, "<span class='notice'>[target.name] woke up already, the implant would be ineffective against him!</span>")
return FALSE
target_mind.add_antag_datum(/datum/antagonist/overthrow, UO.team)
log_combat(user, target, "implanted", "\a [name]")
return TRUE
/obj/item/overthrow_converter/attack(mob/living/carbon/human/M, mob/living/carbon/human/user)
if(!istype(M) || !istype(user))
return
if(!uses)
to_chat(user,"<span class='warning'>The converter is empty!</span>")
return
if(M == user)
to_chat(user,"<span class='warning'>You cannot convert yourself!</span>")
return
if(M.has_trait(TRAIT_MINDSHIELD))
to_chat(user, "<span class='danger'>This mind is too strong to convert, try to remove whatever is protecting it first!</span>")
return
M.visible_message("<span class='warning'>[user] is attempting to implant [M].</span>")
if(do_mob(user, M, 50))
if(convert(M,user))
M.visible_message("[user] has implanted [M].", "<span class='notice'>[user] implants you.</span>")
uses--
update_icon()
else
to_chat(user, "<span class='warning'>[user] fails to implant [M].</span>")
/obj/item/overthrow_converter/update_icon()
if(uses)
icon_state = "implanter1"
else
icon_state = "implanter0"
@@ -0,0 +1,41 @@
/datum/team/overthrow
name = "overthrow" // The team name is set on creation by the leader.
member_name = "syndicate agent"
var/hud_entry_num // A number holding the hud's index inside 'huds' global list. Gets set on hud update, if a hud doesn't exist already. Must be a number, otherwise BYOND shits up with assoc lists and everything goes to hell.
/datum/team/overthrow/Destroy()
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[hud_entry_num]
GLOB.huds -= GLOB.huds[hud_entry_num]
qdel(overthrowhud)
. = ..()
/datum/team/overthrow/proc/create_objectives()
// Heads objective
var/datum/objective/overthrow/heads/heads = new()
heads.team = src
heads.find_target()
objectives += heads
// AI objective
var/datum/objective/overthrow/AI/AI = new()
AI.team = src
AI.update_explanation_text()
objectives += AI
// Target objective
var/datum/objective/overthrow/target/target = new()
target.team = src
target.find_target()
objectives += target
addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE)
/datum/team/overthrow/proc/update_objectives()
var/datum/objective/overthrow/heads/heads_obj = locate() in objectives
if(!heads_obj)
heads_obj = new()
heads_obj.team = src
objectives += heads_obj
for(var/i in members)
var/datum/mind/M = i
M.objectives += heads_obj
heads_obj.find_targets()
addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE)
+18 -40
View File
@@ -45,65 +45,43 @@
/datum/team/pirate/proc/forge_objectives()
var/datum/objective/loot/getbooty = new()
getbooty.team = src
getbooty.storage_area = locate(/area/shuttle/pirate/vault) in GLOB.sortedAreas
getbooty.update_initial_value()
for(var/obj/machinery/computer/piratepad_control/P in GLOB.machines)
var/area/A = get_area(P)
if(istype(A,/area/shuttle/pirate))
getbooty.cargo_hold = P
break
getbooty.update_explanation_text()
objectives += getbooty
for(var/datum/mind/M in members)
M.objectives |= objectives
GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
/obj/structure/reagent_dispensers/beerkeg,
/mob/living/simple_animal/parrot,
/obj/item/stack/sheet/mineral/gold,
/obj/item/stack/sheet/mineral/diamond,
/obj/item/stack/spacecash,
/obj/item/melee/sabre,)))
/datum/objective/loot
var/area/storage_area //Place where we we will look for the loot.
var/obj/machinery/computer/piratepad_control/cargo_hold
explanation_text = "Acquire valuable loot and store it in designated area."
var/target_value = 50000
var/initial_value = 0 //Things in the vault at spawn time do not count
/datum/objective/loot/update_explanation_text()
if(storage_area)
explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name]."
if(cargo_hold)
var/area/storage_area = get_area(cargo_hold)
explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name] cargo hold."
/datum/objective/loot/proc/loot_listing()
//Lists notable loot.
if(!storage_area)
if(!cargo_hold || !cargo_hold.total_report)
return "Nothing"
var/list/loot_table = list()
for(var/atom/movable/AM in storage_area.GetAllContents())
if(is_type_in_typecache(AM,GLOB.pirate_loot_cache))
var/lootname = AM.name
var/count = 1
if(istype(AM,/obj/item/stack)) //Ugh.
var/obj/item/stack/S = AM
lootname = S.singular_name
count = S.amount
if(!loot_table[lootname])
loot_table[lootname] = count
else
loot_table[lootname] += count
cargo_hold.total_report.total_value = sortTim(cargo_hold.total_report.total_value, cmp = /proc/cmp_numeric_dsc, associative = TRUE)
var/count = 0
var/list/loot_texts = list()
for(var/key in loot_table)
var/amount = loot_table[key]
loot_texts += "[amount] [key][amount > 1 ? "s":""]"
for(var/datum/export/E in cargo_hold.total_report.total_value)
if(++count > 5)
break
loot_texts += E.total_printout(cargo_hold.total_report,notes = FALSE)
return loot_texts.Join(", ")
/datum/objective/loot/proc/get_loot_value()
if(!storage_area)
return 0
var/value = 0
for(var/turf/T in storage_area.contents)
value += export_item_and_contents(T,TRUE, TRUE, dry_run = TRUE)
return value - initial_value
/datum/objective/loot/proc/update_initial_value()
initial_value = get_loot_value()
return cargo_hold.points
/datum/objective/loot/check_completion()
return ..() || get_loot_value() >= target_value
@@ -48,6 +48,7 @@
speed = 1
unique_name = TRUE
hud_possible = list(ANTAG_HUD)
hud_type = /datum/hud/revenant
var/essence = 75 //The resource, and health, of revenants.
var/essence_regen_cap = 75 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
@@ -143,10 +144,10 @@
/mob/living/simple_animal/revenant/med_hud_set_status()
return //we use no hud
/mob/living/simple_animal/revenant/say(message)
/mob/living/simple_animal/revenant/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(!message)
return
log_talk(src,"[key_name(src)] : [message]",LOGSAY)
src.log_talk(message, LOG_SAY)
var/rendered = "<span class='revennotice'><b>[src]</b> says, \"[message]\"</span>"
for(var/mob/M in GLOB.mob_list)
if(isrevenant(M))
@@ -199,7 +200,7 @@
if(!essence)
death()
/mob/living/simple_animal/revenant/dust()
/mob/living/simple_animal/revenant/dust(just_ash, drop_items, force)
death()
/mob/living/simple_animal/revenant/gib()
@@ -114,7 +114,7 @@
if(!msg)
charge_counter = charge_max
return
log_talk(user,"RevenantTransmit: [key_name(user)]->[key_name(M)] : [msg]",LOGSAY)
log_directed_talk(user, M, msg, LOG_SAY, "revenant whisper")
to_chat(user, "<span class='revenboldnotice'>You transmit to [M]:</span> <span class='revennotice'>[msg]</span>")
if(!M.anti_magic_check(FALSE, TRUE)) //hear no evil
to_chat(M, "<span class='revenboldnotice'>You hear something behind you talking...</span> <span class='revennotice'>[msg]</span>")
@@ -35,7 +35,7 @@
. = ..()
create_objectives()
equip_rev()
owner.current.log_message("<font color='red'>Has been converted to the revolution!</font>", INDIVIDUAL_ATTACK_LOG)
owner.current.log_message("has been converted to the revolution!", LOG_ATTACK, color="red")
/datum/antagonist/rev/on_removal()
remove_objectives()
@@ -209,7 +209,7 @@
//blunt trauma deconversions call this through species.dm spec_attacked_by()
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!")
log_attack("[key_name(owner.current)] has been deconverted from the revolution by [key_name(deconverter)]!")
if(borged)
message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]")
owner.special_role = null
@@ -281,7 +281,7 @@
var/list/datum/mind/heads = SSjob.get_all_heads()
var/list/sec = SSjob.get_all_sec()
if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((3 - sec.len) / 3)))
if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3)))
var/list/datum/mind/non_heads = members - head_revolutionaries
var/list/datum/mind/promotable = list()
for(var/datum/mind/khrushchev in non_heads)
+2 -1
View File
@@ -98,6 +98,7 @@
del_on_death = 1
deathmessage = "explodes with a sharp pop!"
light_color = LIGHT_COLOR_CYAN
hud_type = /datum/hud/swarmer
var/resources = 0 //Resource points, generated by consuming metal/glass
var/max_resources = 100
@@ -482,7 +483,7 @@
if(ishuman(target) && (!H.handcuffed))
H.handcuffed = new /obj/item/restraints/handcuffs/energy/used(H)
H.update_handcuffed()
add_logs(src, H, "handcuffed")
log_combat(src, H, "handcuffed")
var/datum/effect_system/spark_spread/S = new
S.set_up(4,0,get_turf(target))
@@ -13,6 +13,7 @@
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
can_hijack = HIJACK_HIJACKER
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
@@ -2,6 +2,7 @@
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new
@@ -110,6 +110,25 @@
eat()
return
/obj/singularity/wizard/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
GET_COMPONENT_FROM(insaneinthemembrane, /datum/component/mood, C)
if(insaneinthemembrane.sanity < 15)
return //they've already seen it and are about to die, or are just too insane to care
to_chat(C, "<span class='userdanger'>OH GOD! NONE OF IT IS REAL! NONE OF IT IS REEEEEEEEEEEEEEEEEEEEEEEEAL!</span>")
insaneinthemembrane.sanity = 0
for(var/lore in typesof(/datum/brain_trauma/severe))
C.gain_trauma(lore)
addtimer(CALLBACK(src, /obj/singularity/wizard.proc/deranged, C), 100)
/obj/singularity/wizard/proc/deranged(mob/living/carbon/C)
if(!C || C.stat == DEAD)
return
C.vomit(0, TRUE, TRUE, 3, TRUE)
C.spew_organ(3, 2)
C.death()
/obj/singularity/wizard/mapped/admin_investigate_setup()
return
@@ -282,7 +301,7 @@
switch(user.zone_selected)
if(BODY_ZONE_PRECISE_MOUTH)
var/wgw = sanitize(input(user, "What would you like the victim to say", "Voodoo", null) as text)
target.say(wgw)
target.say(wgw, forced = "voodoo doll")
log_game("[key_name(user)] made [key_name(target)] say [wgw] with a voodoo doll.")
if(BODY_ZONE_PRECISE_EYES)
user.set_machine(src)
@@ -66,7 +66,7 @@
if(iscultist(user))
to_chat(user, "<span class='cultlarge'>\"Come now, do not capture your bretheren's soul.\"</span>")
return
add_logs(user, M, "captured [M.name]'s soul", src)
log_combat(user, M, "captured [M.name]'s soul", src)
transfer_soul("VICTIM", M, user)
///////////////////Options for using captured souls///////////////////////////////////////
@@ -76,7 +76,7 @@
return 0
/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user,obj/item/spellbook/book) //return point value or -1 for failure
var/area/wizard_station/A = locate() in GLOB.sortedAreas
var/area/wizard_station/A = GLOB.areas_by_type[/area/wizard_station]
if(!(user in A.contents))
to_chat(user, "<span class='warning'>You can only refund spells at the wizard lair</span>")
return -1
@@ -316,6 +316,12 @@
cost = 1
category = "Defensive"
/datum/spellbook_entry/item/lockerstaff
name = "Staff of the Locker"
desc = "A staff that shoots lockers. It eats anyone it hits on its way, leaving a welded locker with your victims behind."
item_path = /obj/item/gun/magic/staff/locker
category = "Defensive"
/datum/spellbook_entry/item/scryingorb
name = "Scrying Orb"
desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you X-ray vision."
+2 -1
View File
@@ -12,12 +12,13 @@
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wizard/on_gain()
register()
equip_wizard()
if(give_objectives)
create_objectives()
equip_wizard()
if(move_to_lair)
send_to_lair()
. = ..()
+2
View File
@@ -101,6 +101,8 @@
..()
/obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I)
if(..())
return TRUE
if(toggle_secure())
to_chat(user, "<span class='notice'>\The [src] is ready!</span>")
else
+1 -1
View File
@@ -56,7 +56,7 @@
if(D.secondsElectrified)
D.secondsElectrified = -1
LAZYADD(D.shockedby, "\[[time_stamp()]\] [key_name(usr)]")
add_logs(usr, D, "electrified")
log_combat(usr, D, "electrified")
else
D.secondsElectrified = 0
if(specialfunctions & SAFE)
+15 -7
View File
@@ -1,3 +1,4 @@
#define CONFUSION_STACK_MAX_MULTIPLIER 2
/obj/item/assembly/flash
name = "flash"
desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production."
@@ -86,7 +87,7 @@
if(isturf(target_loc) || (ismob(target_loc) && isturf(target_loc.loc)))
return viewers(range, get_turf(target_loc))
else
return typecache_filter_list(target_loc.GetAllContents(), typecacheof(list(/mob/living)))
return typecache_filter_list(target_loc.GetAllContents(), GLOB.typecache_living)
/obj/item/assembly/flash/proc/try_use_flash(mob/user = null)
if(crit_fail || (world.time < last_trigger + cooldown))
@@ -103,12 +104,17 @@
/obj/item/assembly/flash/proc/flash_carbon(mob/living/carbon/M, mob/user, power = 15, targeted = TRUE, generic_message = FALSE)
if(!istype(M))
return
add_logs(user, M, "[targeted? "flashed(targeted)" : "flashed(AOE)"]", src)
if(user)
log_combat(user, M, "[targeted? "flashed(targeted)" : "flashed(AOE)"]", src)
else //caused by emp/remote signal
M.log_message("was [targeted? "flashed(targeted)" : "flashed(AOE)"]",LOG_ATTACK)
if(generic_message && M != user)
to_chat(M, "<span class='disarm'>[src] emits a blinding light!</span>")
if(targeted)
if(M.flash_act(1, 1))
M.confused = CLAMP(M.confused + power, 0, power * 2)
if(M.confused < power)
var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
M.confused += min(power, diff)
if(user)
terrible_conversion_proc(M, user)
visible_message("<span class='disarm'>[user] blinds [M] with the flash!</span>")
@@ -125,7 +131,8 @@
to_chat(M, "<span class='danger'>[src] fails to blind you!</span>")
else
if(M.flash_act())
M.confused = CLAMP(M.confused + power, 0, power * 2)
var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
M.confused += min(power, diff)
/obj/item/assembly/flash/attack(mob/living/M, mob/user)
if(!try_use_flash(user))
@@ -135,10 +142,11 @@
return TRUE
else if(issilicon(M))
var/mob/living/silicon/robot/R = M
add_logs(user, R, "flashed", src)
log_combat(user, R, "flashed", src)
update_icon(1)
M.Knockdown(rand(80,120))
R.confused = CLAMP(R.confused + 5, 0, 10)
R.Knockdown(rand(80,120))
var/diff = 5 * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
R.confused += min(5, diff)
R.flash_act(affect_silicon = 1)
user.visible_message("<span class='disarm'>[user] overloads [R]'s sensors with the flash!</span>", "<span class='danger'>You overload [R]'s sensors with the flash!</span>")
return TRUE
+4 -4
View File
@@ -8,7 +8,7 @@
var/scanning = FALSE
var/health_scan
var/alarm_health = 0
var/alarm_health = HEALTH_THRESHOLD_CRIT
/obj/item/assembly/health/examine(mob/user)
..()
@@ -31,11 +31,11 @@
return secured
/obj/item/assembly/health/multitool_act(mob/living/user, obj/item/I)
if(alarm_health == 0)
alarm_health = -90
if(alarm_health == HEALTH_THRESHOLD_CRIT)
alarm_health = HEALTH_THRESHOLD_DEAD
to_chat(user, "<span class='notice'>You toggle [src] to \"detect death\" mode.</span>")
else
alarm_health = 0
alarm_health = HEALTH_THRESHOLD_CRIT
to_chat(user, "<span class='notice'>You toggle [src] to \"detect critical state\" mode.</span>")
return TRUE
+2
View File
@@ -97,6 +97,8 @@
a_right.attack_hand()
/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool)
if(..())
return TRUE
to_chat(user, "<span class='notice'>You disassemble [src]!</span>")
if(a_left)
a_left.on_detach()
+2 -2
View File
@@ -164,9 +164,9 @@
/obj/item/assembly/infra/proc/switchListener(turf/newloc)
QDEL_NULL(listener)
listener = newloc.AddComponent(/datum/component/redirect, COMSIG_ATOM_EXITED, CALLBACK(src, .proc/check_exit))
listener = newloc.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_EXITED = CALLBACK(src, .proc/check_exit)))
/obj/item/assembly/infra/proc/check_exit(atom/movable/offender)
/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/offender)
if(QDELETED(src))
return
if(offender == src || istype(offender,/obj/effect/beam/i_beam))

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