Merge remote-tracking branch 'upstream/master' into motivation
This commit is contained in:
@@ -80,8 +80,11 @@
|
||||
var/client/banned_client = banned_mob?.client
|
||||
var/banned_mob_guest_key = had_banned_mob && IsGuestKey(banned_mob.key)
|
||||
banned_mob = null
|
||||
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]'")
|
||||
var/datum/db_query/query_add_ban_get_ckey = SSdbcore.NewQuery({"
|
||||
SELECT 1
|
||||
FROM [format_table_name("player")]
|
||||
WHERE ckey = :ckey"},
|
||||
list("ckey" = ckey))
|
||||
if(!query_add_ban_get_ckey.warn_execute())
|
||||
qdel(query_add_ban_get_ckey)
|
||||
return
|
||||
@@ -122,10 +125,11 @@
|
||||
else
|
||||
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 = '[sql_a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
|
||||
var/datum/db_query/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)
|
||||
"}, list("a_ckey" = a_ckey))
|
||||
if(!query_check_adminban_amt.warn_execute())
|
||||
qdel(query_check_adminban_amt)
|
||||
return
|
||||
@@ -143,13 +147,15 @@
|
||||
computerid = "0"
|
||||
if(!ip)
|
||||
ip = "0.0.0.0"
|
||||
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)
|
||||
var/datum/db_query/query_add_ban = SSdbcore.NewQuery({"
|
||||
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(:internet_address), :port, :round_id, :bantype_str, :reason, :job, :duration, Now() + INTERVAL :expiration_time MINUTE, :ckey, :computerid, INET_ATON(:ip), :a_ckey, :a_computerid, INET_ATON(:a_ip), :who, :adminwho)
|
||||
"}, list("internet_address" = world.internet_address ? world.internet_address : 0,
|
||||
"port" = world.port, "round_id" = GLOB.round_id, "bantype_str" = bantype_str, "reason" = reason,
|
||||
"job" = job, "duration" = duration ? "[duration]":"0", "expiration_time" = (duration > 0) ? duration : 0,
|
||||
"ckey" = ckey, "computerid" = computerid, "ip" = ip,
|
||||
"a_ckey" = a_ckey, "a_computerid" = a_computerid, "a_ip" = a_ip, "who" = who, "adminwho" = adminwho
|
||||
))
|
||||
if(!query_add_ban.warn_execute())
|
||||
qdel(query_add_ban)
|
||||
return
|
||||
@@ -160,7 +166,7 @@
|
||||
var/datum/admin_help/AH = admin_ticket_log(ckey, msg)
|
||||
|
||||
if(announceinirc)
|
||||
send2irc("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
|
||||
send2adminchat("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
|
||||
|
||||
if(kickbannedckey)
|
||||
if(AH)
|
||||
@@ -212,11 +218,11 @@
|
||||
bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )"
|
||||
else
|
||||
bantype_sql = "bantype = '[bantype_str]'"
|
||||
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)"
|
||||
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = :ckey AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
|
||||
var/list/sql_args = list("ckey" = ckey)
|
||||
if(job)
|
||||
var/sql_job = sanitizeSQL(job)
|
||||
sql += " AND job = '[sql_job]'"
|
||||
sql += " AND job = :job"
|
||||
sql_args["job"] = job
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
@@ -224,7 +230,7 @@
|
||||
var/ban_id
|
||||
var/ban_number = 0 //failsafe
|
||||
|
||||
var/datum/DBQuery/query_unban_get_id = SSdbcore.NewQuery(sql)
|
||||
var/datum/db_query/query_unban_get_id = SSdbcore.NewQuery(sql, sql_args)
|
||||
if(!query_unban_get_id.warn_execute())
|
||||
qdel(query_unban_get_id)
|
||||
return
|
||||
@@ -258,7 +264,7 @@
|
||||
to_chat(usr, "Cancelled")
|
||||
return
|
||||
|
||||
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]")
|
||||
var/datum/db_query/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
|
||||
@@ -278,19 +284,17 @@
|
||||
return
|
||||
qdel(query_edit_ban_get_details)
|
||||
|
||||
reason = sanitizeSQL(reason)
|
||||
var/value
|
||||
|
||||
switch(param)
|
||||
if("reason")
|
||||
if(!value)
|
||||
value = input("Insert the new reason for [p_key]'s ban", "New Reason", "[reason]", null) as null|text
|
||||
value = sanitizeSQL(value)
|
||||
if(!value)
|
||||
to_chat(usr, "Cancelled")
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_edit_ban_reason = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [e_key] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
|
||||
var/datum/db_query/query_edit_ban_reason = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [e_key] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
|
||||
if(!query_edit_ban_reason.warn_execute())
|
||||
qdel(query_edit_ban_reason)
|
||||
return
|
||||
@@ -303,7 +307,7 @@
|
||||
to_chat(usr, "Cancelled")
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_edit_ban_duration = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [e_key] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
|
||||
var/datum/db_query/query_edit_ban_duration = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [e_key] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
|
||||
if(!query_edit_ban_duration.warn_execute())
|
||||
qdel(query_edit_ban_duration)
|
||||
return
|
||||
@@ -333,7 +337,7 @@
|
||||
var/ban_number = 0 //failsafe
|
||||
|
||||
var/p_key
|
||||
var/datum/DBQuery/query_unban_get_ckey = SSdbcore.NewQuery(sql)
|
||||
var/datum/db_query/query_unban_get_ckey = SSdbcore.NewQuery(sql)
|
||||
if(!query_unban_get_ckey.warn_execute())
|
||||
qdel(query_unban_get_ckey)
|
||||
return
|
||||
@@ -358,7 +362,7 @@
|
||||
var/unban_ip = owner.address
|
||||
|
||||
var/sql_update = "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = INET_ATON('[unban_ip]') WHERE id = [id]"
|
||||
var/datum/DBQuery/query_unban = SSdbcore.NewQuery(sql_update)
|
||||
var/datum/db_query/query_unban = SSdbcore.NewQuery(sql_update)
|
||||
if(!query_unban.warn_execute())
|
||||
qdel(query_unban)
|
||||
return
|
||||
@@ -448,20 +452,28 @@
|
||||
|
||||
if(adminckey || playerckey || ip || cid)
|
||||
var/list/searchlist = list()
|
||||
var/list/searchlist_args = list()
|
||||
if(playerckey)
|
||||
searchlist += "ckey = '[sanitizeSQL(ckey(playerckey))]'"
|
||||
searchlist += "ckey = :playerckey"
|
||||
searchlist_args["playerckey"] = playerckey
|
||||
if(adminckey)
|
||||
searchlist += "a_ckey = '[sanitizeSQL(ckey(adminckey))]'"
|
||||
searchlist += "a_ckey = :adminckey"
|
||||
searchlist_args["adminckey"] = adminckey
|
||||
if(ip)
|
||||
searchlist += "ip = INET_ATON('[sanitizeSQL(ip)]')"
|
||||
searchlist += "ip = INET_ATON(:ip)"
|
||||
searchlist_args["ip"] = ip
|
||||
if(cid)
|
||||
searchlist += "computerid = '[sanitizeSQL(cid)]'"
|
||||
var/search = searchlist.Join(" AND ")
|
||||
searchlist += "computerid = :cid"
|
||||
searchlist_args["cid"] = cid
|
||||
var/search = searchlist.Join(" AND ") // x = x AND y = z
|
||||
var/bancount = 0
|
||||
var/bansperpage = 15
|
||||
var/pagecount = 0
|
||||
page = text2num(page)
|
||||
var/datum/DBQuery/query_count_bans = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("ban")] WHERE [search]")
|
||||
var/datum/db_query/query_count_bans = SSdbcore.NewQuery({"
|
||||
SELECT COUNT(id) FROM [format_table_name("ban")]
|
||||
WHERE [search]
|
||||
"}, searchlist_args)
|
||||
if(!query_count_bans.warn_execute())
|
||||
qdel(query_count_bans)
|
||||
return
|
||||
@@ -489,7 +501,11 @@
|
||||
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, 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]")
|
||||
|
||||
var/datum/db_query/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]"}, searchlist_args)
|
||||
if(!query_search_bans.warn_execute())
|
||||
qdel(query_search_bans)
|
||||
return
|
||||
|
||||
@@ -100,7 +100,18 @@
|
||||
if(computer_id)
|
||||
cidquery = " OR computerid = '[computer_id]' "
|
||||
|
||||
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)")
|
||||
var/datum/db_query/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)
|
||||
"}, list(
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_ban_check.Execute(async = TRUE))
|
||||
qdel(query_ban_check)
|
||||
key_cache[key] = 0
|
||||
|
||||
@@ -119,16 +119,12 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
set waitfor = FALSE
|
||||
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
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]"))
|
||||
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
|
||||
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
|
||||
|
||||
//load our rank - > rights associations
|
||||
@@ -160,7 +156,7 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
if(!no_update)
|
||||
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")]")
|
||||
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
|
||||
if(!query_load_admin_ranks.Execute())
|
||||
message_admins("Error loading admin ranks from database. Loading from backup.")
|
||||
log_sql("Error loading admin ranks from database. Loading from backup.")
|
||||
@@ -168,7 +164,7 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
else
|
||||
while(query_load_admin_ranks.NextRow())
|
||||
var/skip
|
||||
var/rank_name = ckeyEx(query_load_admin_ranks.item[1])
|
||||
var/rank_name = query_load_admin_ranks.item[1]
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == rank_name) //this rank was already loaded from txt override
|
||||
skip = 1
|
||||
@@ -234,20 +230,12 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
rank_names[R.name] = R
|
||||
//ckeys listed in admins.txt are always made admins before sql loading is attempted
|
||||
var/list/lines = world.file2list("[global.config.directory]/admins.txt")
|
||||
for(var/line in lines)
|
||||
if(!length(line) || findtextEx(line, "#", 1, 2))
|
||||
continue
|
||||
var/list/entry = splittext(line, "=")
|
||||
if(entry.len < 2)
|
||||
continue
|
||||
var/ckey = ckey(entry[1])
|
||||
var/rank = ckeyEx(entry[2])
|
||||
if(!ckey || !rank)
|
||||
continue
|
||||
new /datum/admins(rank_names[rank], ckey, 0, 1)
|
||||
var/admins_text = file2text("[global.config.directory]/admins.txt")
|
||||
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
|
||||
while(admins_regex.Find(admins_text))
|
||||
new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE)
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
|
||||
var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")] ORDER BY rank")
|
||||
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank` FROM [format_table_name("admin")] ORDER BY `rank`")
|
||||
if(!query_load_admins.Execute())
|
||||
message_admins("Error loading admins from database. Loading from backup.")
|
||||
log_sql("Error loading admins from database. Loading from backup.")
|
||||
@@ -255,7 +243,7 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
else
|
||||
while(query_load_admins.NextRow())
|
||||
var/admin_ckey = ckey(query_load_admins.item[1])
|
||||
var/admin_rank = ckeyEx(query_load_admins.item[2])
|
||||
var/admin_rank = query_load_admins.item[2]
|
||||
var/skip
|
||||
if(rank_names[admin_rank] == null)
|
||||
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
|
||||
|
||||
@@ -6,13 +6,21 @@ GLOBAL_PROTECT(admin_verbs_default)
|
||||
return list(
|
||||
/client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
|
||||
/client/proc/cmd_admin_say, /*admin-only ooc chat*/
|
||||
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
|
||||
/client/proc/deadchat,
|
||||
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
|
||||
/client/proc/hide_verbs, /*hides all our adminverbs*/
|
||||
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
|
||||
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
|
||||
/client/proc/toggleprayers,
|
||||
/client/proc/toggleadminhelpsound,
|
||||
/client/proc/debugstatpanel,
|
||||
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
|
||||
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
|
||||
/client/proc/secrets,
|
||||
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
|
||||
/client/proc/reload_admins,
|
||||
/client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
|
||||
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
|
||||
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
|
||||
/client/proc/stop_sounds,
|
||||
/client/proc/mark_datum_mapview,
|
||||
/client/proc/debugstatpanel
|
||||
// /client/proc/fix_air /*resets air in designated radius to its default atmos composition*/
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
|
||||
GLOBAL_PROTECT(admin_verbs_admin)
|
||||
@@ -24,6 +32,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/datum/verbs/menu/Admin/verb/playerpanel,
|
||||
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
|
||||
/client/proc/check_ai_laws, /*shows AI and borg laws*/
|
||||
// /client/proc/ghost_pool_protection, /*opens a menu for toggling ghost roles*/
|
||||
/datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
|
||||
/datum/admins/proc/toggleooclocal, /*toggles looc on/off for everyone*/
|
||||
/datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
|
||||
@@ -35,15 +44,15 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/
|
||||
/client/proc/toggle_view_range, /*changes how far we can see*/
|
||||
/client/proc/getserverlogs, /*for accessing server logs*/
|
||||
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
|
||||
/client/proc/cmd_admin_headset_message, /*send an message to somebody through their headset as CentCom*/
|
||||
/client/proc/getcurrentlogs, /*for accessing server logs for the current round*/
|
||||
/client/proc/cmd_admin_subtle_message, /*send a message to somebody as a 'voice in their head'*/
|
||||
/client/proc/cmd_admin_headset_message, /*send a message to somebody through their headset as CentCom*/
|
||||
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
|
||||
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
|
||||
/client/proc/centcom_podlauncher,/*Open a window to launch a Supplypod and configure it or it's contents*/
|
||||
/client/proc/check_antagonists, /*shows all antags*/
|
||||
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
|
||||
/client/proc/jumptocoord, /*we ghost and jump to a coordinate*/
|
||||
/client/proc/getcurrentlogs, /*for accessing server logs for the current round*/
|
||||
/client/proc/Getmob, /*teleports a mob to our location*/
|
||||
/client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/
|
||||
// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage
|
||||
@@ -53,6 +62,8 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
|
||||
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
|
||||
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/
|
||||
// /client/proc/admin_disable_shuttle, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/
|
||||
// /client/proc/admin_enable_shuttle, /*undoes the above*/
|
||||
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
|
||||
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
|
||||
/client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/
|
||||
@@ -64,25 +75,19 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud
|
||||
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
|
||||
/datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */
|
||||
/client/proc/deadchat,
|
||||
/client/proc/toggleprayers,
|
||||
// /client/proc/toggle_prayer_sound,
|
||||
// /client/proc/colorasay,
|
||||
// /client/proc/resetasaycolor,
|
||||
/client/proc/toggleadminhelpsound,
|
||||
/client/proc/respawn_character,
|
||||
/client/proc/secrets,
|
||||
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
|
||||
/client/proc/reload_admins,
|
||||
/client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
|
||||
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
|
||||
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
|
||||
/client/proc/panicbunker,
|
||||
/client/proc/addbunkerbypass,
|
||||
/client/proc/revokebunkerbypass,
|
||||
/client/proc/stop_sounds,
|
||||
/client/proc/mark_datum_mapview,
|
||||
/client/proc/hide_verbs, /*hides all our adminverbs*/
|
||||
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
|
||||
/datum/admins/proc/open_borgopanel,
|
||||
/client/proc/admin_cmd_respawn_return_to_lobby,
|
||||
/client/proc/admin_cmd_remove_ghost_respawn_timer
|
||||
/client/proc/admin_cmd_respawn_return_to_lobby, //CIT
|
||||
/client/proc/admin_cmd_remove_ghost_respawn_timer, //CIT
|
||||
/client/proc/addbunkerbypass, //CIT
|
||||
/client/proc/revokebunkerbypass, //CIT
|
||||
/datum/admins/proc/open_borgopanel
|
||||
)
|
||||
|
||||
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
|
||||
GLOBAL_PROTECT(admin_verbs_ban)
|
||||
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
|
||||
@@ -110,13 +115,14 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
|
||||
/client/proc/show_tip,
|
||||
/client/proc/smite,
|
||||
/client/proc/admin_away,
|
||||
/client/proc/cmd_admin_toggle_fov,
|
||||
/client/proc/cmd_admin_toggle_fov, //CIT CHANGE - FOV
|
||||
/client/proc/roll_dices //CIT CHANGE - Adds dice verb
|
||||
))
|
||||
GLOBAL_PROTECT(admin_verbs_fun)
|
||||
GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character))
|
||||
GLOBAL_PROTECT(admin_verbs_spawn)
|
||||
GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
|
||||
GLOBAL_PROTECT(admin_verbs_server)
|
||||
/world/proc/AVerbsServer()
|
||||
return list(
|
||||
/datum/admins/proc/startnow,
|
||||
@@ -126,17 +132,20 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
|
||||
/datum/admins/proc/toggleaban,
|
||||
/client/proc/everyone_random,
|
||||
/datum/admins/proc/toggleAI,
|
||||
/datum/admins/proc/toggleMulticam,
|
||||
/datum/admins/proc/toggledynamicvote,
|
||||
/datum/admins/proc/toggleMulticam, //CIT
|
||||
/datum/admins/proc/toggledynamicvote, //CIT
|
||||
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
|
||||
/client/proc/cmd_debug_del_all,
|
||||
/client/proc/toggle_random_events,
|
||||
/client/proc/forcerandomrotate,
|
||||
/client/proc/adminchangemap,
|
||||
/client/proc/toggle_hub
|
||||
/client/proc/panicbunker,
|
||||
// /client/proc/toggle_interviews,
|
||||
/client/proc/toggle_hub,
|
||||
/client/proc/toggle_cdn
|
||||
)
|
||||
GLOBAL_PROTECT(admin_verbs_server)
|
||||
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
|
||||
GLOBAL_PROTECT(admin_verbs_debug)
|
||||
/world/proc/AVerbsDebug()
|
||||
return list(
|
||||
/client/proc/restart_controller,
|
||||
@@ -174,27 +183,37 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
|
||||
/client/proc/cmd_display_init_log,
|
||||
/client/proc/cmd_display_overlay_log,
|
||||
/client/proc/reload_configuration,
|
||||
// /client/proc/atmos_control,
|
||||
// /client/proc/reload_cards,
|
||||
// /client/proc/validate_cards,
|
||||
// /client/proc/test_cardpack_distribution,
|
||||
// /client/proc/print_cards,
|
||||
// #ifdef TESTING
|
||||
// /client/proc/check_missing_sprites,
|
||||
// #endif
|
||||
/datum/admins/proc/create_or_modify_area,
|
||||
#ifdef REFERENCE_TRACKING
|
||||
/datum/admins/proc/view_refs,
|
||||
/datum/admins/proc/view_del_failures,
|
||||
#endif
|
||||
/client/proc/generate_wikichem_list, //DO NOT PRESS UNLESS YOU WANT SUPERLAG
|
||||
// /client/proc/check_timer_sources,
|
||||
/client/proc/toggle_cdn,
|
||||
/client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG
|
||||
)
|
||||
GLOBAL_PROTECT(admin_verbs_debug)
|
||||
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
|
||||
GLOBAL_PROTECT(admin_verbs_possess)
|
||||
GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions))
|
||||
GLOBAL_PROTECT(admin_verbs_permissions)
|
||||
GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/create_poll))
|
||||
GLOBAL_PROTECT(admin_verbs_poll)
|
||||
|
||||
//verbs which can be hidden - needs work
|
||||
GLOBAL_PROTECT(admin_verbs_poll)
|
||||
GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/set_ooc,
|
||||
/client/proc/reset_ooc,
|
||||
/client/proc/deadmin,
|
||||
/datum/admins/proc/show_traitor_panel,
|
||||
// /datum/admins/proc/show_skill_panel,
|
||||
/datum/admins/proc/toggleenter,
|
||||
/datum/admins/proc/toggleguests,
|
||||
/datum/admins/proc/announce,
|
||||
@@ -239,7 +258,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/Debug2,
|
||||
/client/proc/reload_admins,
|
||||
/client/proc/cmd_debug_make_powernets,
|
||||
/client/proc/startSinglo,
|
||||
/client/proc/startSinglo, // tg removed this
|
||||
/client/proc/cmd_debug_mob_lists,
|
||||
/client/proc/cmd_debug_del_all,
|
||||
/client/proc/enable_debug_verbs,
|
||||
@@ -247,8 +266,9 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/proc/release,
|
||||
/client/proc/reload_admins,
|
||||
/client/proc/panicbunker,
|
||||
/client/proc/addbunkerbypass,
|
||||
/client/proc/revokebunkerbypass,
|
||||
/client/proc/addbunkerbypass, //CIT
|
||||
/client/proc/revokebunkerbypass, //CIT
|
||||
// /client/proc/toggle_interviews,
|
||||
/client/proc/admin_change_sec_level,
|
||||
/client/proc/toggle_nuke,
|
||||
/client/proc/cmd_display_del_log,
|
||||
@@ -322,7 +342,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
remove_verb(src, /client/proc/hide_most_verbs)
|
||||
add_verb(src, /client/proc/show_verbs)
|
||||
|
||||
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>")
|
||||
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide Most Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return
|
||||
|
||||
@@ -333,7 +353,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
remove_admin_verbs()
|
||||
add_verb(src, /client/proc/show_verbs)
|
||||
|
||||
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>")
|
||||
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return
|
||||
|
||||
@@ -344,7 +364,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
remove_verb(src, /client/proc/show_verbs)
|
||||
add_admin_verbs()
|
||||
|
||||
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>")
|
||||
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -354,7 +374,8 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
set category = "Admin.Game"
|
||||
set name = "Aghost"
|
||||
if(!holder)
|
||||
return FALSE
|
||||
return
|
||||
. = TRUE
|
||||
if(isobserver(mob))
|
||||
//re-enter
|
||||
var/mob/dead/observer/ghost = mob
|
||||
@@ -367,7 +388,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
ghost.reenter_corpse()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
else if(isnewplayer(mob))
|
||||
to_chat(src, "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>")
|
||||
to_chat(src, "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>", confidential = TRUE)
|
||||
return FALSE
|
||||
else
|
||||
//ghostize
|
||||
@@ -375,10 +396,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
message_admins("[key_name_admin(usr)] admin ghosted.")
|
||||
var/mob/body = mob
|
||||
body.ghostize(1, voluntary = TRUE)
|
||||
init_verbs()
|
||||
if(body && !body.key)
|
||||
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return TRUE
|
||||
|
||||
/client/proc/invisimin()
|
||||
set name = "Invisimin"
|
||||
@@ -387,10 +408,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
if(holder && mob)
|
||||
if(mob.invisibility == INVISIBILITY_OBSERVER)
|
||||
mob.invisibility = initial(mob.invisibility)
|
||||
to_chat(mob, "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>")
|
||||
to_chat(mob, "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>", confidential = TRUE)
|
||||
else
|
||||
mob.invisibility = INVISIBILITY_OBSERVER
|
||||
to_chat(mob, "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>")
|
||||
to_chat(mob, "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>", confidential = TRUE)
|
||||
|
||||
/client/proc/check_antagonists()
|
||||
set name = "Check Antagonists"
|
||||
@@ -403,8 +424,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Antagonists") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/unban_panel()
|
||||
set name = "Unban Panel"
|
||||
set name = "Unbanning Panel"
|
||||
set category = "Admin"
|
||||
if(!check_rights(R_BAN))
|
||||
return
|
||||
if(holder)
|
||||
if(CONFIG_GET(flag/ban_legacy_system))
|
||||
holder.unbanpanel()
|
||||
@@ -412,6 +435,14 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
holder.DB_ban_panel()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Unban Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
// /client/proc/ban_panel()
|
||||
// set name = "Banning Panel"
|
||||
// set category = "Admin"
|
||||
// if(!check_rights(R_BAN))
|
||||
// return
|
||||
// holder.ban_panel()
|
||||
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Banning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/game_panel()
|
||||
set name = "Game Panel"
|
||||
set category = "Admin.Game"
|
||||
@@ -419,13 +450,13 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
holder.Game()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/secrets()
|
||||
set name = "Secrets"
|
||||
set category = "Admin.Game"
|
||||
if (holder)
|
||||
holder.Secrets()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
// /client/proc/poll_panel()
|
||||
// set name = "Server Poll Management"
|
||||
// set category = "Admin"
|
||||
// if(!check_rights(R_POLL))
|
||||
// return
|
||||
// holder.poll_list_panel()
|
||||
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Server Poll Management") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/findStealthKey(txt)
|
||||
if(txt)
|
||||
@@ -457,7 +488,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
if(isobserver(mob))
|
||||
mob.invisibility = initial(mob.invisibility)
|
||||
mob.alpha = initial(mob.alpha)
|
||||
mob.name = initial(mob.name)
|
||||
if(mob.mind)
|
||||
mob.name = mob.mind.name
|
||||
else
|
||||
mob.name = mob.real_name
|
||||
mob.mouse_opacity = initial(mob.mouse_opacity)
|
||||
else
|
||||
var/new_key = ckeyEx(stripped_input(usr, "Enter your desired display name.", "Fake Key", key, 26))
|
||||
@@ -485,7 +519,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
|
||||
switch(choice)
|
||||
if(null)
|
||||
return 0
|
||||
return
|
||||
if("Small Bomb (1, 2, 3, 3)")
|
||||
explosion(epicenter, 1, 2, 3, 3, TRUE, TRUE)
|
||||
if("Medium Bomb (2, 3, 4, 4)")
|
||||
@@ -538,7 +572,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
if (isnull(ex_power))
|
||||
return
|
||||
var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE)
|
||||
to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])")
|
||||
to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])", confidential = TRUE)
|
||||
|
||||
/client/proc/get_dynex_power()
|
||||
set category = "Debug"
|
||||
@@ -549,7 +583,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
if (isnull(ex_range))
|
||||
return
|
||||
var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE)
|
||||
to_chat(usr, "Estimated Explosive Power: [power]")
|
||||
to_chat(usr, "Estimated Explosive Power: [power]", confidential = TRUE)
|
||||
|
||||
/client/proc/set_dynex_scale()
|
||||
set category = "Debug"
|
||||
@@ -563,6 +597,55 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
log_admin("[key_name(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
|
||||
message_admins("[key_name_admin(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
|
||||
|
||||
// /client/proc/atmos_control()
|
||||
// set name = "Atmos Control Panel"
|
||||
// set category = "Debug"
|
||||
// if(!check_rights(R_DEBUG))
|
||||
// return
|
||||
// SSair.ui_interact(mob)
|
||||
|
||||
// /client/proc/reload_cards()
|
||||
// set name = "Reload Cards"
|
||||
// set category = "Debug"
|
||||
// if(!check_rights(R_DEBUG))
|
||||
// return
|
||||
// if(!SStrading_card_game.loaded)
|
||||
// message_admins("The card subsystem is not currently loaded")
|
||||
// return
|
||||
// reloadAllCardFiles(SStrading_card_game.card_files, SStrading_card_game.card_directory)
|
||||
|
||||
// /client/proc/validate_cards()
|
||||
// set name = "Validate Cards"
|
||||
// set category = "Debug"
|
||||
// if(!check_rights(R_DEBUG))
|
||||
// return
|
||||
// if(!SStrading_card_game.loaded)
|
||||
// message_admins("The card subsystem is not currently loaded")
|
||||
// return
|
||||
// var/message = checkCardpacks(SStrading_card_game.card_packs)
|
||||
// message += checkCardDatums()
|
||||
// if(message)
|
||||
// message_admins(message)
|
||||
|
||||
// /client/proc/test_cardpack_distribution()
|
||||
// set name = "Test Cardpack Distribution"
|
||||
// set category = "Debug"
|
||||
// if(!check_rights(R_DEBUG))
|
||||
// return
|
||||
// if(!SStrading_card_game.loaded)
|
||||
// message_admins("The card subsystem is not currently loaded")
|
||||
// return
|
||||
// var/pack = input("Which pack should we test?", "You fucked it didn't you") as null|anything in sortList(SStrading_card_game.card_packs)
|
||||
// var/batchCount = input("How many times should we open it?", "Don't worry, I understand") as null|num
|
||||
// var/batchSize = input("How many cards per batch?", "I hope you remember to check the validation") as null|num
|
||||
// var/guar = input("Should we use the pack's guaranteed rarity? If so, how many?", "We've all been there. Man you should have seen the old system") as null|num
|
||||
// checkCardDistribution(pack, batchSize, batchCount, guar)
|
||||
|
||||
// /client/proc/print_cards()
|
||||
// set name = "Print Cards"
|
||||
// set category = "Debug"
|
||||
// printAllCards()
|
||||
|
||||
/client/proc/give_spell(mob/T in GLOB.mob_list)
|
||||
set category = "Admin.Fun"
|
||||
set name = "Give Spell"
|
||||
@@ -572,13 +655,13 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
var/type_length = length_char("/obj/effect/proc_holder/spell") + 2
|
||||
for(var/A in GLOB.spells)
|
||||
spell_list[copytext_char("[A]", type_length)] = A
|
||||
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list
|
||||
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list)
|
||||
if(!S)
|
||||
return
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the spell [S].</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name_admin(T)] the spell [S].</span>")
|
||||
|
||||
S = spell_list[S]
|
||||
if(T.mind)
|
||||
@@ -592,12 +675,12 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
set name = "Remove Spell"
|
||||
set desc = "Remove a spell from the selected mob."
|
||||
|
||||
if(T && T.mind)
|
||||
var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in T.mind.spell_list
|
||||
if(T?.mind)
|
||||
var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list)
|
||||
if(S)
|
||||
T.mind.RemoveSpell(S)
|
||||
log_admin("[key_name(usr)] removed the spell [S] from [key_name(T)].")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed the spell [S] from [key_name(T)].</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed the spell [S] from [key_name_admin(T)].</span>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Remove Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/give_disease(mob/living/T in GLOB.mob_living_list)
|
||||
@@ -605,15 +688,15 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
set name = "Give Disease"
|
||||
set desc = "Gives a Disease to a mob."
|
||||
if(!istype(T))
|
||||
to_chat(src, "<span class='notice'>You can only give a disease to a mob of type /mob/living.</span>")
|
||||
to_chat(src, "<span class='notice'>You can only give a disease to a mob of type /mob/living.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in SSdisease.diseases
|
||||
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sortList(SSdisease.diseases, /proc/cmp_typepaths_asc)
|
||||
if(!D)
|
||||
return
|
||||
T.ForceContractDisease(new D, FALSE, TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the disease [D].</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name_admin(T)] the disease [D].</span>")
|
||||
|
||||
/client/proc/object_say(obj/O in world)
|
||||
set category = "Admin.Events"
|
||||
@@ -655,8 +738,8 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
holder.deactivate()
|
||||
|
||||
to_chat(src, "<span class='interface'>You are now a normal player.</span>")
|
||||
log_admin("[src] deadmined themself.")
|
||||
message_admins("[src] deadmined themself.")
|
||||
log_admin("[src] deadminned themselves.")
|
||||
message_admins("[src] deadminned themselves.")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin")
|
||||
|
||||
/client/proc/readmin()
|
||||
@@ -679,7 +762,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
if (!holder)
|
||||
return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force
|
||||
|
||||
to_chat(src, "<span class='interface'>You are now an admin.</span>")
|
||||
to_chat(src, "<span class='interface'>You are now an admin.</span>", confidential = TRUE)
|
||||
message_admins("[src] re-adminned themselves.")
|
||||
log_admin("[src] re-adminned themselves.")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin")
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
if(!M || !istype(M) || !M.ckey)
|
||||
return FALSE
|
||||
|
||||
if(!M.client) //no cache. fallback to a datum/DBQuery
|
||||
var/datum/DBQuery/query_jobban_check_ban = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(M.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[sanitizeSQL(rank)]'")
|
||||
if(!M.client) //no cache. fallback to a datum/db_query
|
||||
var/datum/db_query/query_jobban_check_ban = SSdbcore.NewQuery({"
|
||||
SELECT reason FROM [format_table_name("ban")]
|
||||
WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :rank
|
||||
"}, list("ckey" = M.ckey, "rank" = rank))
|
||||
if(!query_jobban_check_ban.warn_execute())
|
||||
qdel(query_jobban_check_ban)
|
||||
return
|
||||
@@ -28,7 +31,10 @@
|
||||
return
|
||||
if(C && istype(C))
|
||||
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)")
|
||||
var/datum/db_query/query_jobban_build_cache = SSdbcore.NewQuery({"
|
||||
SELECT job, reason FROM [format_table_name("ban")]
|
||||
WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)
|
||||
"}, list("ckey" = C.ckey))
|
||||
if(!query_jobban_build_cache.warn_execute())
|
||||
qdel(query_jobban_build_cache)
|
||||
return
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/client/proc/callproc()
|
||||
set category = "Debug"
|
||||
set name = "Advanced ProcCall"
|
||||
@@ -21,7 +20,7 @@
|
||||
return
|
||||
target = value["value"]
|
||||
if(!istype(target))
|
||||
to_chat(usr, "<span class='danger'>Invalid target.</span>")
|
||||
to_chat(usr, "<span class='danger'>Invalid target.</span>", confidential = TRUE)
|
||||
return
|
||||
if("No")
|
||||
target = null
|
||||
@@ -41,12 +40,12 @@
|
||||
|
||||
if(targetselected)
|
||||
if(!hascall(target, procname))
|
||||
to_chat(usr, "<span class='warning'>Error: callproc(): type [target.type] has no [proctype] named [procpath].</span>")
|
||||
to_chat(usr, "<span class='warning'>Error: callproc(): type [target.type] has no [proctype] named [procpath].</span>", confidential = TRUE)
|
||||
return
|
||||
else
|
||||
procpath = "/[proctype]/[procname]"
|
||||
if(!text2path(procpath))
|
||||
to_chat(usr, "<span class='warning'>Error: callproc(): [procpath] does not exist.</span>")
|
||||
to_chat(usr, "<span class='warning'>Error: callproc(): [procpath] does not exist.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/lst = get_callproc_args()
|
||||
@@ -55,24 +54,24 @@
|
||||
|
||||
if(targetselected)
|
||||
if(!target)
|
||||
to_chat(usr, "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>")
|
||||
to_chat(usr, "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>", confidential = TRUE)
|
||||
return
|
||||
var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
|
||||
log_admin(msg)
|
||||
message_admins(msg) //Proccall announce removed.
|
||||
message_admins(msg) //Proccall announce removed.
|
||||
admin_ticket_log(target, msg)
|
||||
returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
|
||||
else
|
||||
//this currently has no hascall protection. wasn't able to get it working.
|
||||
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
|
||||
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed.
|
||||
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed.
|
||||
returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
if(get_retval)
|
||||
get_retval += returnval
|
||||
. = get_callproc_returnval(returnval, procname)
|
||||
if(.)
|
||||
to_chat(usr, .)
|
||||
to_chat(usr, ., confidential = TRUE)
|
||||
|
||||
GLOBAL_VAR(AdminProcCaller)
|
||||
GLOBAL_PROTECT(AdminProcCaller)
|
||||
@@ -84,34 +83,30 @@ GLOBAL_VAR(LastAdminCalledTarget)
|
||||
GLOBAL_PROTECT(LastAdminCalledTarget)
|
||||
GLOBAL_VAR(LastAdminCalledProc)
|
||||
GLOBAL_PROTECT(LastAdminCalledProc)
|
||||
GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
|
||||
GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
|
||||
/// Wrapper for proccalls where the datum is flagged as vareditted
|
||||
/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
|
||||
if(target != GLOBAL_PROC && procname == "Del")
|
||||
to_chat(usr, "<span class='warning'>Calling Del() is not allowed</span>")
|
||||
if(target && procname == "Del")
|
||||
to_chat(usr, "Calling Del() is not allowed", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(target != GLOBAL_PROC && !target.CanProcCall(procname))
|
||||
to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
|
||||
to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!", confidential = TRUE)
|
||||
return
|
||||
var/current_caller = GLOB.AdminProcCaller
|
||||
var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
|
||||
if(!ckey)
|
||||
CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
|
||||
|
||||
if(current_caller && current_caller != ckey)
|
||||
if(!GLOB.AdminProcCallSpamPrevention[ckey])
|
||||
to_chat(usr, "<span class='adminnotice'>Another set of admin called procs are still running, your proc will be run after theirs finish.</span>")
|
||||
GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
|
||||
UNTIL(!GLOB.AdminProcCaller)
|
||||
to_chat(usr, "<span class='adminnotice'>Running your proc</span>")
|
||||
GLOB.AdminProcCallSpamPrevention -= ckey
|
||||
else
|
||||
UNTIL(!GLOB.AdminProcCaller)
|
||||
// hey kev i removed the sleep here because it blocks this proc
|
||||
to_chat(usr, "<span class='adminnotice'>Another set of admin called procs are still running. Try again later.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
GLOB.LastAdminCalledProc = procname
|
||||
if(target != GLOBAL_PROC)
|
||||
GLOB.LastAdminCalledTargetRef = "[REF(target)]"
|
||||
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
|
||||
GLOB.LastAdminCalledTargetRef = REF(target)
|
||||
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
|
||||
++GLOB.AdminProcCallCount
|
||||
. = world.WrapAdminProcCall(target, procname, arguments)
|
||||
if(--GLOB.AdminProcCallCount == 0)
|
||||
@@ -120,11 +115,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
//adv proc call this, ya nerds
|
||||
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
|
||||
if(target == GLOBAL_PROC)
|
||||
return call(text2path("/proc/[procname]"))(arglist(arguments))
|
||||
return call("/proc/[procname]")(arglist(arguments))
|
||||
else if(target != world)
|
||||
return call(target, procname)(arglist(arguments))
|
||||
else
|
||||
log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
|
||||
log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
|
||||
|
||||
/proc/IsAdminAdvancedProcCall()
|
||||
#ifdef TESTING
|
||||
@@ -145,17 +140,17 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
if(!procname)
|
||||
return
|
||||
if(!hascall(A,procname))
|
||||
to_chat(usr, "<span class='warning'>Error: callproc_datum(): type [A.type] has no proc named [procname].</span>")
|
||||
to_chat(usr, "<font color='red'>Error: callproc_datum(): type [A.type] has no proc named [procname].</font>", confidential = TRUE)
|
||||
return
|
||||
var/list/lst = get_callproc_args()
|
||||
if(!lst)
|
||||
return
|
||||
|
||||
if(!A || !IsValidSrc(A))
|
||||
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>")
|
||||
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>", confidential = TRUE)
|
||||
return
|
||||
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
|
||||
var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
|
||||
log_admin(msg)
|
||||
message_admins(msg)
|
||||
admin_ticket_log(A, msg)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -163,7 +158,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
|
||||
. = get_callproc_returnval(returnval,procname)
|
||||
if(.)
|
||||
to_chat(usr, .)
|
||||
to_chat(usr, ., confidential = TRUE)
|
||||
|
||||
/client/proc/get_callproc_args()
|
||||
var/argnum = input("Number of arguments","Number:",0) as num|null
|
||||
@@ -188,7 +183,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
. = ""
|
||||
if(islist(returnval))
|
||||
var/list/returnedlist = returnval
|
||||
. = "<span class='notice'>"
|
||||
. = "<font color='blue'>"
|
||||
if(returnedlist.len)
|
||||
var/assoc_check = returnedlist[1]
|
||||
if(istext(assoc_check) && (returnedlist[assoc_check] != null))
|
||||
@@ -202,7 +197,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
. += "\n[elem]"
|
||||
else
|
||||
. = "[procname] returned an empty list"
|
||||
. += "</span>"
|
||||
. += "</font>"
|
||||
|
||||
else
|
||||
. = "<span class='notice'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</span>"
|
||||
. = "<font color='blue'>[procname] returned: [!isnull(returnval) ? html_encode(returnval) : "null"]</font>"
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
var/endtime = input("Set end time for poll 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 starting time for obvious reasons.", "Set end time", SQLtime()) as text
|
||||
if(!endtime)
|
||||
return
|
||||
endtime = sanitizeSQL(endtime)
|
||||
var/datum/DBQuery/query_validate_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[endtime]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[endtime]','%Y-%c-%d %T'), 0)")
|
||||
var/datum/db_query/query_validate_time = SSdbcore.NewQuery({"
|
||||
SELECT IF(STR_TO_DATE(:endtime,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:endtime,'%Y-%c-%d %T'), 0)
|
||||
"}, list("endtime" = endtime))
|
||||
if(!query_validate_time.warn_execute() || QDELETED(usr) || !src)
|
||||
qdel(query_validate_time)
|
||||
return
|
||||
@@ -63,11 +64,9 @@
|
||||
dontshow = 0
|
||||
else
|
||||
return
|
||||
var/sql_ckey = sanitizeSQL(ckey)
|
||||
var/question = input("Write your question","Question") as message|null
|
||||
if(!question)
|
||||
return
|
||||
question = sanitizeSQL(question)
|
||||
var/list/sql_option_list = list()
|
||||
if(polltype != POLLTYPE_TEXT)
|
||||
var/add_option = 1
|
||||
@@ -75,7 +74,6 @@
|
||||
var/option = input("Write your option","Option") as message|null
|
||||
if(!option)
|
||||
return
|
||||
option = sanitizeSQL(option)
|
||||
var/default_percentage_calc = 0
|
||||
if(polltype != POLLTYPE_IRV)
|
||||
switch(alert("Should this option be included by default when poll result percentages are generated?",,"Yes","No","Cancel"))
|
||||
@@ -92,34 +90,27 @@
|
||||
var/descmax = ""
|
||||
if(polltype == POLLTYPE_RATING)
|
||||
minval = input("Set minimum rating value.","Minimum rating") as num|null
|
||||
if(minval)
|
||||
minval = sanitizeSQL(minval)
|
||||
else if(minval == null)
|
||||
if(minval == null)
|
||||
return
|
||||
maxval = input("Set maximum rating value.","Maximum rating") as num|null
|
||||
if(maxval)
|
||||
maxval = sanitizeSQL(maxval)
|
||||
if(minval >= maxval)
|
||||
to_chat(src, "Maximum rating value can't be less than or equal to minimum rating value")
|
||||
continue
|
||||
else if(maxval == null)
|
||||
if(maxval == null)
|
||||
return
|
||||
descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message|null
|
||||
if(descmin)
|
||||
descmin = sanitizeSQL(descmin)
|
||||
else if(descmin == null)
|
||||
if(descmin == null)
|
||||
return
|
||||
descmid = input("Optional: Set description for median rating","Median rating description") as message|null
|
||||
if(descmid)
|
||||
descmid = sanitizeSQL(descmid)
|
||||
else if(descmid == null)
|
||||
if(descmid == null)
|
||||
return
|
||||
descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message|null
|
||||
if(descmax)
|
||||
descmax = sanitizeSQL(descmax)
|
||||
else if(descmax == null)
|
||||
if(descmax == null)
|
||||
return
|
||||
sql_option_list += list(list("text" = "'[option]'", "minval" = "'[minval]'", "maxval" = "'[maxval]'", "descmin" = "'[descmin]'", "descmid" = "'[descmid]'", "descmax" = "'[descmax]'", "default_percentage_calc" = "'[default_percentage_calc]'"))
|
||||
sql_option_list += list(list(
|
||||
"text" = option, "minval" = minval, "maxval" = maxval,
|
||||
"descmin" = descmin, "descmid" = descmid, "descmax" = descmax,
|
||||
"default_percentage_calc" = default_percentage_calc))
|
||||
switch(alert(" ",,"Add option","Finish", "Cancel"))
|
||||
if("Add option")
|
||||
add_option = 1
|
||||
@@ -129,14 +120,21 @@
|
||||
return 0
|
||||
var/m1 = "[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]"
|
||||
var/m2 = "[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]<br>Question: [question]"
|
||||
var/datum/DBQuery/query_polladd_question = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', INET_ATON('[address]'), '[dontshow]')")
|
||||
var/datum/db_query/query_polladd_question = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow)
|
||||
VALUES (:polltype, :starttime, :endtime, :question, :adminonly, :choice_amount, :ckey, INET_ATON(:address), :dontshow)
|
||||
"}, list(
|
||||
"polltype" = polltype, "starttime" = starttime, "endtime" = endtime,
|
||||
"question" = question, "adminonly" = adminonly, "choice_amount" = choice_amount,
|
||||
"ckey" = ckey, "address" = address, "dontshow" = dontshow
|
||||
))
|
||||
if(!query_polladd_question.warn_execute())
|
||||
qdel(query_polladd_question)
|
||||
return
|
||||
qdel(query_polladd_question)
|
||||
if(polltype != POLLTYPE_TEXT)
|
||||
var/pollid = 0
|
||||
var/datum/DBQuery/query_get_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
|
||||
var/datum/db_query/query_get_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
|
||||
if(!query_get_id.warn_execute())
|
||||
qdel(query_get_id)
|
||||
return
|
||||
@@ -145,6 +143,6 @@
|
||||
qdel(query_get_id)
|
||||
for(var/list/i in sql_option_list)
|
||||
i |= list("pollid" = "'[pollid]'")
|
||||
SSdbcore.MassInsert(format_table_name("poll_option"), sql_option_list, warn = 1)
|
||||
SSdbcore.MassInsert(format_table_name("poll_option"), sql_option_list, warn = TRUE)
|
||||
log_admin(m1)
|
||||
message_admins(m2)
|
||||
|
||||
@@ -28,6 +28,8 @@ GLOBAL_PROTECT(href_token)
|
||||
|
||||
var/deadmined
|
||||
|
||||
var/datum/filter_editor/filteriffic
|
||||
|
||||
/datum/admins/CanProcCall(procname)
|
||||
. = ..()
|
||||
if(!check_rights(R_SENSITIVE))
|
||||
|
||||
@@ -29,27 +29,27 @@
|
||||
return
|
||||
if (!bypasscache)
|
||||
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
|
||||
if (cachedintel && cachedintel.is_valid())
|
||||
if (cachedintel?.is_valid())
|
||||
cachedintel.cache = TRUE
|
||||
return cachedintel
|
||||
|
||||
if(SSdbcore.Connect())
|
||||
var/rating_bad = CONFIG_GET(number/ipintel_rating_bad)
|
||||
var/datum/DBQuery/query_get_ip_intel = SSdbcore.NewQuery({"
|
||||
var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
|
||||
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
|
||||
FROM [format_table_name("ipintel")]
|
||||
WHERE
|
||||
ip = INET_ATON('[ip]')
|
||||
ip = INET_ATON(':ip')
|
||||
AND ((
|
||||
intel < [rating_bad]
|
||||
intel < :rating_bad
|
||||
AND
|
||||
date + INTERVAL [CONFIG_GET(number/ipintel_save_good)] HOUR > NOW()
|
||||
date + INTERVAL :save_good HOUR > NOW()
|
||||
) OR (
|
||||
intel >= [rating_bad]
|
||||
intel >= :rating_bad
|
||||
AND
|
||||
date + INTERVAL [CONFIG_GET(number/ipintel_save_bad)] HOUR > NOW()
|
||||
date + INTERVAL :save_bad HOUR > NOW()
|
||||
))
|
||||
"})
|
||||
"}, list("ip" = ip, "rating_bad" = rating_bad, "save_good" = CONFIG_GET(number/ipintel_save_good), "save_bad" = CONFIG_GET(number/ipintel_save_bad)))
|
||||
if(!query_get_ip_intel.Execute())
|
||||
qdel(query_get_ip_intel)
|
||||
return
|
||||
@@ -67,12 +67,15 @@
|
||||
if (updatecache && res.intel >= 0)
|
||||
SSipintel.cache[ip] = res
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_add_ip_intel = SSdbcore.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
|
||||
var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON(:ip), :intel) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()",
|
||||
list("ip" = ip, "intel" = res.intel)
|
||||
)
|
||||
query_add_ip_intel.Execute()
|
||||
qdel(query_add_ip_intel)
|
||||
|
||||
|
||||
/proc/ip_intel_query(ip, var/retryed=0)
|
||||
/proc/ip_intel_query(ip, retryed=0)
|
||||
. = -1 //default
|
||||
if (!ip)
|
||||
return
|
||||
@@ -131,8 +134,3 @@
|
||||
/proc/log_ipintel(text)
|
||||
log_game("IPINTEL: [text]")
|
||||
debug_admins("IPINTEL: [text]")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,21 +15,14 @@
|
||||
else
|
||||
output += "<br><a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a><br><a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a>"
|
||||
if(action == 1)
|
||||
var/list/searchlist = list(" WHERE ")
|
||||
if(target)
|
||||
searchlist += "ckey = '[sanitizeSQL(target)]'"
|
||||
if(operation)
|
||||
if(target)
|
||||
searchlist += " AND "
|
||||
searchlist += "operation = '[sanitizeSQL(operation)]'"
|
||||
var/search
|
||||
if(searchlist.len > 1)
|
||||
search = searchlist.Join("")
|
||||
var/logcount = 0
|
||||
var/logssperpage = 20
|
||||
var/pagecount = 0
|
||||
page = text2num(page)
|
||||
var/datum/DBQuery/query_count_admin_logs = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("admin_log")][search]")
|
||||
var/datum/db_query/query_count_admin_logs = SSdbcore.NewQuery(
|
||||
"SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)",
|
||||
list("target" = target, "operation" = operation)
|
||||
)
|
||||
if(!query_count_admin_logs.warn_execute())
|
||||
qdel(query_count_admin_logs)
|
||||
return
|
||||
@@ -43,8 +36,20 @@
|
||||
logcount -= logssperpage
|
||||
pagecount++
|
||||
output += "|"
|
||||
var/limit = " LIMIT [logssperpage * page], [logssperpage]"
|
||||
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]")
|
||||
var/datum/db_query/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
|
||||
WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation)
|
||||
ORDER BY datetime DESC
|
||||
LIMIT :skip, :take
|
||||
"}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage))
|
||||
if(!query_search_admin_logs.warn_execute())
|
||||
qdel(query_search_admin_logs)
|
||||
return
|
||||
@@ -59,7 +64,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 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")
|
||||
var/datum/db_query/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
|
||||
@@ -70,7 +75,7 @@
|
||||
output += "<hr style='background:#000000; border:0; height:1px'>"
|
||||
qdel(query_check_admin_errors)
|
||||
output += "<h3>Unused ranks</h3>"
|
||||
var/datum/DBQuery/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].rank = [format_table_name("admin_ranks")].rank WHERE [format_table_name("admin")].rank IS NULL")
|
||||
var/datum/db_query/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].`rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].`rank` = [format_table_name("admin_ranks")].`rank` WHERE [format_table_name("admin")].`rank` IS NULL")
|
||||
if(!query_check_unused_rank.warn_execute())
|
||||
qdel(query_check_unused_rank)
|
||||
return
|
||||
@@ -130,7 +135,7 @@
|
||||
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
|
||||
permissions_assets.send(src)
|
||||
@@ -145,19 +150,19 @@
|
||||
skip = TRUE
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
|
||||
if(admin_ckey in GLOB.protected_admins)
|
||||
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>", confidential = TRUE)
|
||||
return
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
|
||||
if(D.rank in GLOB.protected_ranks)
|
||||
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>", confidential = TRUE)
|
||||
return
|
||||
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions"))
|
||||
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>", confidential = TRUE)
|
||||
legacy_only = TRUE
|
||||
if(check_rights(R_DBRANKS, FALSE))
|
||||
if(!skip)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
|
||||
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>", confidential = TRUE)
|
||||
use_db = FALSE
|
||||
else
|
||||
use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel")
|
||||
@@ -165,7 +170,6 @@
|
||||
return
|
||||
if(use_db == "Permanent")
|
||||
use_db = TRUE
|
||||
admin_ckey = sanitizeSQL(admin_ckey)
|
||||
else
|
||||
use_db = FALSE
|
||||
if(QDELETED(usr))
|
||||
@@ -209,26 +213,34 @@
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins))
|
||||
to_chat(usr, "<span class='danger'>[admin_key] is already an admin.</span>")
|
||||
to_chat(usr, "<span class='danger'>[admin_key] is already an admin.</span>", confidential = TRUE)
|
||||
return FALSE
|
||||
if(use_db)
|
||||
. = sanitizeSQL(.)
|
||||
//if an admin exists without a datum they won't be caught by the above
|
||||
var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE ckey = '[.]'")
|
||||
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
|
||||
"SELECT 1 FROM [format_table_name("admin")] WHERE ckey = :ckey",
|
||||
list("ckey" = .)
|
||||
)
|
||||
if(!query_admin_in_db.warn_execute())
|
||||
qdel(query_admin_in_db)
|
||||
return FALSE
|
||||
if(query_admin_in_db.NextRow())
|
||||
qdel(query_admin_in_db)
|
||||
to_chat(usr, "<span class='danger'>[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.</span>")
|
||||
to_chat(usr, "<span class='danger'>[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.</span>", confidential = TRUE)
|
||||
return FALSE
|
||||
qdel(query_admin_in_db)
|
||||
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')")
|
||||
var/datum/db_query/query_add_admin = SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES (:ckey, 'NEW ADMIN')",
|
||||
list("ckey" = .)
|
||||
)
|
||||
if(!query_add_admin.warn_execute())
|
||||
qdel(query_add_admin)
|
||||
return FALSE
|
||||
qdel(query_add_admin)
|
||||
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', '[.]', 'New admin added: [.]')")
|
||||
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target))
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = .))
|
||||
if(!query_add_admin_log.warn_execute())
|
||||
qdel(query_add_admin_log)
|
||||
return FALSE
|
||||
@@ -243,12 +255,18 @@
|
||||
var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
|
||||
var/m2 = "[key_name(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
|
||||
if(use_db)
|
||||
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
|
||||
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
|
||||
"DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey",
|
||||
list("ckey" = admin_ckey)
|
||||
)
|
||||
if(!query_add_rank.warn_execute())
|
||||
qdel(query_add_rank)
|
||||
return
|
||||
qdel(query_add_rank)
|
||||
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', '[admin_ckey]', 'Admin removed: [admin_ckey]')")
|
||||
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey))
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = admin_ckey))
|
||||
if(!query_add_rank_log.warn_execute())
|
||||
qdel(query_add_rank_log)
|
||||
return
|
||||
@@ -271,6 +289,14 @@
|
||||
log_admin("[key_name(usr)] forcefully deadmined [admin_key]")
|
||||
D.deactivate() //after logs so the deadmined admin can see the message.
|
||||
|
||||
/datum/admins/proc/auto_deadmin()
|
||||
to_chat(owner, "<span class='interface'>You are now a normal player.</span>", confidential = TRUE)
|
||||
var/old_owner = owner
|
||||
deactivate()
|
||||
message_admins("[old_owner] deadmined via auto-deadmin config.")
|
||||
log_admin("[old_owner] deadmined via auto-deadmin config.")
|
||||
return TRUE
|
||||
|
||||
/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only)
|
||||
var/datum/admin_rank/R
|
||||
var/list/rank_names = list()
|
||||
@@ -281,7 +307,7 @@
|
||||
rank_names[R.name] = R
|
||||
var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names
|
||||
if(new_rank == "*New Rank*")
|
||||
new_rank = ckeyEx(input("Please input a new rank", "New custom rank") as text|null)
|
||||
new_rank = input("Please input a new rank", "New custom rank") as text|null
|
||||
if(!new_rank)
|
||||
return
|
||||
R = rank_names[new_rank]
|
||||
@@ -294,10 +320,12 @@
|
||||
var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]"
|
||||
var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]"
|
||||
if(use_db)
|
||||
new_rank = sanitizeSQL(new_rank)
|
||||
//if a player was tempminned before having a permanent change made to their rank they won't yet be in the db
|
||||
var/old_rank
|
||||
var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
|
||||
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
|
||||
"SELECT `rank` FROM [format_table_name("admin")] WHERE ckey = :admin_ckey",
|
||||
list("admin_ckey" = admin_ckey)
|
||||
)
|
||||
if(!query_admin_in_db.warn_execute())
|
||||
qdel(query_admin_in_db)
|
||||
return
|
||||
@@ -308,29 +336,44 @@
|
||||
old_rank = query_admin_in_db.item[1]
|
||||
qdel(query_admin_in_db)
|
||||
//similarly if a temp rank is created it won't be in the db if someone is permanently changed to it
|
||||
var/datum/DBQuery/query_rank_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin_ranks")] WHERE rank = '[new_rank]'")
|
||||
var/datum/db_query/query_rank_in_db = SSdbcore.NewQuery(
|
||||
"SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank",
|
||||
list("new_rank" = new_rank)
|
||||
)
|
||||
if(!query_rank_in_db.warn_execute())
|
||||
qdel(query_rank_in_db)
|
||||
return
|
||||
if(!query_rank_in_db.NextRow())
|
||||
QDEL_NULL(query_rank_in_db)
|
||||
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_flags) VALUES ('[new_rank]', '0', '0', '0')")
|
||||
var/datum/db_query/query_add_rank = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags)
|
||||
VALUES (:new_rank, '0', '0', '0')
|
||||
"}, list("new_rank" = new_rank))
|
||||
if(!query_add_rank.warn_execute())
|
||||
qdel(query_add_rank)
|
||||
return
|
||||
qdel(query_add_rank)
|
||||
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', '[new_rank]', 'New rank added: [new_rank]')")
|
||||
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank))
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = new_rank))
|
||||
if(!query_add_rank_log.warn_execute())
|
||||
qdel(query_add_rank_log)
|
||||
return
|
||||
qdel(query_add_rank_log)
|
||||
qdel(query_rank_in_db)
|
||||
var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'")
|
||||
var/datum/db_query/query_change_rank = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey",
|
||||
list("new_rank" = new_rank, "admin_ckey" = admin_ckey)
|
||||
)
|
||||
if(!query_change_rank.warn_execute())
|
||||
qdel(query_change_rank)
|
||||
return
|
||||
qdel(query_change_rank)
|
||||
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', '[admin_ckey]', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')")
|
||||
var/datum/db_query/query_change_rank_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank))
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = new_rank))
|
||||
if(!query_change_rank_log.warn_execute())
|
||||
qdel(query_change_rank_log)
|
||||
return
|
||||
@@ -357,11 +400,15 @@
|
||||
return
|
||||
var/m1 = "[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]"
|
||||
var/m2 = "[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]"
|
||||
if(use_db || legacy_only)
|
||||
if(use_db && !legacy_only)
|
||||
var/rank_name = D.rank.name
|
||||
var/old_flags
|
||||
var/old_exclude_flags
|
||||
var/old_can_edit_flags
|
||||
var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'")
|
||||
var/datum/db_query/query_get_rank_flags = SSdbcore.NewQuery(
|
||||
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE `rank` = :rank_name",
|
||||
list("rank_name" = rank_name)
|
||||
)
|
||||
if(!query_get_rank_flags.warn_execute())
|
||||
qdel(query_get_rank_flags)
|
||||
return
|
||||
@@ -370,12 +417,19 @@
|
||||
old_exclude_flags = text2num(query_get_rank_flags.item[2])
|
||||
old_can_edit_flags = text2num(query_get_rank_flags.item[3])
|
||||
qdel(query_get_rank_flags)
|
||||
var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'")
|
||||
var/datum/db_query/query_change_rank_flags = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("admin_ranks")] SET flags = :new_flags, exclude_flags = :new_exclude_flags, can_edit_flags = :new_can_edit_flags WHERE `rank` = :rank_name",
|
||||
list("new_flags" = new_flags, "new_exclude_flags" = new_exclude_flags, "new_can_edit_flags" = new_can_edit_flags, "rank_name" = rank_name)
|
||||
)
|
||||
if(!query_change_rank_flags.warn_execute())
|
||||
qdel(query_change_rank_flags)
|
||||
return
|
||||
qdel(query_change_rank_flags)
|
||||
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', '[D.rank.name]', 'Permissions of [D.rank.name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')")
|
||||
var/log_message = "Permissions of [rank_name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]"
|
||||
var/datum/db_query/query_change_rank_flags_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change rank flags', :rank_name, :log)
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "rank_name" = rank_name, "log" = log_message))
|
||||
if(!query_change_rank_flags_log.warn_execute())
|
||||
qdel(query_change_rank_flags_log)
|
||||
return
|
||||
@@ -418,33 +472,41 @@
|
||||
return
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == admin_rank && (!(R.rights & usr.client.holder.rank.can_edit_rights) == R.rights))
|
||||
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>", confidential = TRUE)
|
||||
return
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks))
|
||||
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>", confidential = TRUE)
|
||||
return
|
||||
if(CONFIG_GET(flag/load_legacy_ranks_only))
|
||||
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>")
|
||||
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>", confidential = TRUE)
|
||||
return
|
||||
admin_rank = sanitizeSQL(admin_rank)
|
||||
var/datum/DBQuery/query_admins_with_rank = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE rank = '[admin_rank]'")
|
||||
var/datum/db_query/query_admins_with_rank = SSdbcore.NewQuery(
|
||||
"SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = :admin_rank",
|
||||
list("admin_rank" = admin_rank)
|
||||
)
|
||||
if(!query_admins_with_rank.warn_execute())
|
||||
qdel(query_admins_with_rank)
|
||||
return
|
||||
if(query_admins_with_rank.NextRow())
|
||||
qdel(query_admins_with_rank)
|
||||
to_chat(usr, "<span class='danger'>Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.</span>")
|
||||
to_chat(usr, "<span class='danger'>Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.</span>", confidential = TRUE)
|
||||
return
|
||||
qdel(query_admins_with_rank)
|
||||
if(alert("Are you sure you want to remove [admin_rank]?","Confirm Removal","Do it","Cancel") == "Do it")
|
||||
var/m1 = "[key_name_admin(usr)] removed rank [admin_rank] permanently"
|
||||
var/m2 = "[key_name(usr)] removed rank [admin_rank] permanently"
|
||||
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin_ranks")] WHERE rank = '[admin_rank]'")
|
||||
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
|
||||
"DELETE FROM [format_table_name("admin_ranks")] WHERE `rank` = :admin_rank",
|
||||
list("admin_rank" = admin_rank)
|
||||
)
|
||||
if(!query_add_rank.warn_execute())
|
||||
qdel(query_add_rank)
|
||||
return
|
||||
qdel(query_add_rank)
|
||||
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove rank', '[admin_rank]', 'Rank removed: [admin_rank]')")
|
||||
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
|
||||
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank))
|
||||
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_rank" = admin_rank))
|
||||
if(!query_add_rank_log.warn_execute())
|
||||
qdel(query_add_rank_log)
|
||||
return
|
||||
@@ -455,11 +517,13 @@
|
||||
/datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D)
|
||||
var/sqlrank = "Player"
|
||||
if (D)
|
||||
sqlrank = sanitizeSQL(D.rank.name)
|
||||
admin_ckey = sanitizeSQL(admin_ckey)
|
||||
var/datum/DBQuery/query_sync_lastadminrank = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sqlrank]' WHERE ckey = '[admin_ckey]'")
|
||||
sqlrank = D.rank.name
|
||||
var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("player")] SET lastadminrank = :rank WHERE ckey = :ckey",
|
||||
list("rank" = sqlrank, "ckey" = admin_ckey)
|
||||
)
|
||||
if(!query_sync_lastadminrank.warn_execute())
|
||||
qdel(query_sync_lastadminrank)
|
||||
return
|
||||
qdel(query_sync_lastadminrank)
|
||||
to_chat(usr, "<span class='admin'>Sync of [admin_key] successful.</span>")
|
||||
to_chat(usr, "<span class='admin'>Sync of [admin_key] successful.</span>", confidential = TRUE)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/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>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
if(!type)
|
||||
return
|
||||
@@ -9,8 +9,11 @@
|
||||
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)
|
||||
return
|
||||
var/new_ckey = sanitizeSQL(ckey(new_key))
|
||||
var/datum/DBQuery/query_find_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'")
|
||||
var/new_ckey = ckey(new_key)
|
||||
var/datum/db_query/query_find_ckey = SSdbcore.NewQuery(
|
||||
"SELECT ckey FROM [format_table_name("player")] WHERE ckey = :ckey",
|
||||
list("ckey" = new_ckey)
|
||||
)
|
||||
if(!query_find_ckey.warn_execute())
|
||||
qdel(query_find_ckey)
|
||||
return
|
||||
@@ -23,29 +26,24 @@
|
||||
target_key = new_key
|
||||
if(QDELETED(usr))
|
||||
return
|
||||
if(target_ckey)
|
||||
target_ckey = sanitizeSQL(target_ckey)
|
||||
if(!target_key)
|
||||
target_key = target_ckey
|
||||
if(!admin_ckey)
|
||||
admin_ckey = usr.ckey
|
||||
if(!admin_ckey)
|
||||
return
|
||||
admin_ckey = sanitizeSQL(admin_ckey)
|
||||
if(!target_ckey)
|
||||
target_ckey = admin_ckey
|
||||
if(!text)
|
||||
text = input(usr,"Write your [type]","Create [type]") as null|message
|
||||
if(!text)
|
||||
return
|
||||
text = sanitizeSQL(text)
|
||||
if(!timestamp)
|
||||
timestamp = SQLtime()
|
||||
if(!server)
|
||||
var/ssqlname = CONFIG_GET(string/serversqlname)
|
||||
if (ssqlname)
|
||||
server = ssqlname
|
||||
server = sanitizeSQL(server)
|
||||
if(isnull(secret))
|
||||
switch(alert("Hide note from being viewed by players?", "Secret note?","Yes","No","Cancel"))
|
||||
if("Yes")
|
||||
@@ -59,15 +57,17 @@
|
||||
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)")
|
||||
var/datum/db_query/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)",
|
||||
list("expire_time" = expire_time)
|
||||
)
|
||||
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.")
|
||||
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE)
|
||||
qdel(query_validate_expire_time)
|
||||
return
|
||||
expiry = query_validate_expire_time.item[1]
|
||||
@@ -76,8 +76,23 @@
|
||||
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/datum/db_query/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(:internet_address), :port, :round_id, :secret, :expiry, :note_severity)
|
||||
"}, list(
|
||||
"type" = type,
|
||||
"target_ckey" = target_ckey,
|
||||
"admin_ckey" = admin_ckey,
|
||||
"text" = text,
|
||||
"timestamp" = timestamp,
|
||||
"server" = server,
|
||||
"internet_address" = world.internet_address || "0",
|
||||
"port" = "[world.port]",
|
||||
"round_id" = GLOB.round_id,
|
||||
"secret" = secret,
|
||||
"expiry" = expiry || null,
|
||||
"note_severity" = note_severity,
|
||||
))
|
||||
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())
|
||||
@@ -96,7 +111,7 @@
|
||||
|
||||
/proc/delete_message(message_id, logged = 1, browse)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
message_id = text2num(message_id)
|
||||
if(!message_id)
|
||||
@@ -106,7 +121,11 @@
|
||||
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, 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")
|
||||
var/deleted_by_ckey = usr.ckey
|
||||
var/datum/db_query/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 = :id AND deleted = 0",
|
||||
list("id" = message_id)
|
||||
)
|
||||
if(!query_find_del_message.warn_execute())
|
||||
qdel(query_find_del_message)
|
||||
return
|
||||
@@ -115,7 +134,10 @@
|
||||
target_key = query_find_del_message.item[2]
|
||||
text = query_find_del_message.item[3]
|
||||
qdel(query_find_del_message)
|
||||
var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET deleted = 1 WHERE id = [message_id]")
|
||||
var/datum/db_query/query_del_message = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("messages")] SET deleted = 1, deleted_ckey = :deleted_ckey WHERE id = :id",
|
||||
list("deleted_ckey" = deleted_by_ckey, "id" = message_id)
|
||||
)
|
||||
if(!query_del_message.warn_execute())
|
||||
qdel(query_del_message)
|
||||
return
|
||||
@@ -132,16 +154,24 @@
|
||||
|
||||
/proc/edit_message(message_id, browse)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
message_id = text2num(message_id)
|
||||
if(!message_id)
|
||||
return
|
||||
var/editor_ckey = sanitizeSQL(usr.ckey)
|
||||
var/editor_key = sanitizeSQL(usr.key)
|
||||
var/editor_ckey = usr.ckey
|
||||
var/editor_key = usr.key
|
||||
var/kn = key_name(usr)
|
||||
var/kna = key_name_admin(usr)
|
||||
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")
|
||||
var/datum/db_query/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 = :id AND deleted = 0
|
||||
"}, list("id" = message_id))
|
||||
if(!query_find_edit_message.warn_execute())
|
||||
qdel(query_find_edit_message)
|
||||
return
|
||||
@@ -154,9 +184,12 @@
|
||||
if(!new_text)
|
||||
qdel(query_find_edit_message)
|
||||
return
|
||||
new_text = sanitizeSQL(new_text)
|
||||
var/edit_text = sanitizeSQL("Edited by [editor_key] on [SQLtime()] from<br>[old_text]<br>to<br>[new_text]<hr>")
|
||||
var/datum/DBQuery/query_edit_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET text = '[new_text]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
|
||||
var/edit_text = "Edited by [editor_key] on [SQLtime()] from<br>[old_text]<br>to<br>[new_text]<hr>"
|
||||
var/datum/db_query/query_edit_message = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("messages")]
|
||||
SET text = :text, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
|
||||
WHERE id = :id AND deleted = 0
|
||||
"}, list("text" = new_text, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
|
||||
if(!query_edit_message.warn_execute())
|
||||
qdel(query_edit_message)
|
||||
return
|
||||
@@ -171,16 +204,24 @@
|
||||
|
||||
/proc/edit_message_expiry(message_id, browse)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
message_id = text2num(message_id)
|
||||
if(!message_id)
|
||||
return
|
||||
var/editor_ckey = sanitizeSQL(usr.ckey)
|
||||
var/editor_key = sanitizeSQL(usr.key)
|
||||
var/editor_ckey = usr.ckey
|
||||
var/editor_key = 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")
|
||||
var/datum/db_query/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 = :id AND deleted = 0
|
||||
"}, list("id" = message_id))
|
||||
if(!query_find_edit_expiry_message.warn_execute())
|
||||
qdel(query_find_edit_expiry_message)
|
||||
return
|
||||
@@ -197,8 +238,9 @@
|
||||
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)")
|
||||
var/datum/db_query/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)
|
||||
"}, list("expire_time" = expire_time))
|
||||
if(!query_validate_expire_time_edit.warn_execute())
|
||||
qdel(query_validate_expire_time_edit)
|
||||
qdel(query_find_edit_expiry_message)
|
||||
@@ -206,14 +248,18 @@
|
||||
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.")
|
||||
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE)
|
||||
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")
|
||||
var/edit_text = "Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]<hr>"
|
||||
var/datum/db_query/query_edit_message_expiry = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("messages")]
|
||||
SET expire_timestamp = :expire_time, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
|
||||
WHERE id = :id AND deleted = 0
|
||||
"}, list("expire_time" = (expire_time == "-1" ? null : new_expiry), "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
|
||||
if(!query_edit_message_expiry.warn_execute())
|
||||
qdel(query_edit_message_expiry)
|
||||
qdel(query_find_edit_expiry_message)
|
||||
@@ -229,14 +275,22 @@
|
||||
|
||||
/proc/edit_message_severity(message_id)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
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")
|
||||
var/datum/db_query/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 = :id AND deleted = 0
|
||||
"}, list("id" = message_id))
|
||||
if(!query_find_edit_note_severity.warn_execute())
|
||||
qdel(query_find_edit_note_severity)
|
||||
return
|
||||
@@ -247,15 +301,19 @@
|
||||
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/editor_key = usr.key
|
||||
var/editor_ckey = 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")
|
||||
new_severity = new_severity
|
||||
var/edit_text = "Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]<hr>"
|
||||
var/datum/db_query/query_edit_note_severity = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("messages")]
|
||||
SET severity = :severity, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
|
||||
WHERE id = :id AND deleted = 0
|
||||
"}, list("severity" = new_severity, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
|
||||
if(!query_edit_note_severity.warn_execute(async = TRUE))
|
||||
qdel(query_edit_note_severity)
|
||||
qdel(qdel(query_find_edit_note_severity))
|
||||
@@ -268,16 +326,24 @@
|
||||
|
||||
/proc/toggle_message_secrecy(message_id)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
message_id = text2num(message_id)
|
||||
if(!message_id)
|
||||
return
|
||||
var/editor_ckey = sanitizeSQL(usr.ckey)
|
||||
var/editor_key = sanitizeSQL(usr.key)
|
||||
var/editor_ckey = usr.ckey
|
||||
var/editor_key = usr.key
|
||||
var/kn = key_name(usr)
|
||||
var/kna = key_name_admin(usr)
|
||||
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")
|
||||
var/datum/db_query/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 = :id AND deleted = 0
|
||||
"}, list("id" = message_id))
|
||||
if(!query_find_message_secret.warn_execute())
|
||||
qdel(query_find_message_secret)
|
||||
return
|
||||
@@ -287,7 +353,11 @@
|
||||
var/admin_key = query_find_message_secret.item[3]
|
||||
var/secret = text2num(query_find_message_secret.item[4])
|
||||
var/edit_text = "Made [secret ? "not secret" : "secret"] by [editor_key] on [SQLtime()]<hr>"
|
||||
var/datum/DBQuery/query_message_secret = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET secret = NOT secret, lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id]")
|
||||
var/datum/db_query/query_message_secret = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("messages")]
|
||||
SET secret = NOT secret, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
|
||||
WHERE id = :id
|
||||
"}, list("lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
|
||||
if(!query_message_secret.warn_execute())
|
||||
qdel(query_find_message_secret)
|
||||
qdel(query_message_secret)
|
||||
@@ -298,11 +368,9 @@
|
||||
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
|
||||
qdel(query_find_message_secret)
|
||||
|
||||
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE, override = FALSE)
|
||||
if((!override || IsAdminAdvancedProcCall()) && !check_rights(R_SENSITIVE))
|
||||
return
|
||||
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
var/list/output = list()
|
||||
var/ruler = "<hr style='background:#000000; border:0; height:3px'>"
|
||||
@@ -329,7 +397,20 @@
|
||||
else
|
||||
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, 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)")
|
||||
var/datum/db_query/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)
|
||||
"}, list("type" = type))
|
||||
if(!query_get_type_messages.warn_execute())
|
||||
qdel(query_get_type_messages)
|
||||
return
|
||||
@@ -362,9 +443,24 @@
|
||||
output += "<br>[text]<hr style='background:#000000; border:0; height:1px'>"
|
||||
qdel(query_get_type_messages)
|
||||
if(target_ckey)
|
||||
target_ckey = sanitizeSQL(target_ckey)
|
||||
var/target_key
|
||||
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")
|
||||
var/datum/db_query/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 = :targetckey AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)
|
||||
ORDER BY timestamp DESC
|
||||
"}, list("targetckey" = target_ckey))
|
||||
if(!query_get_messages.warn_execute())
|
||||
qdel(query_get_messages)
|
||||
return
|
||||
@@ -442,7 +538,9 @@
|
||||
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]'")
|
||||
var/datum/db_query/query_get_message_key = SSdbcore.NewQuery({"
|
||||
SELECT byond_key FROM [format_table_name("player")] WHERE ckey = :ckey
|
||||
"}, list("ckey" = target_ckey))
|
||||
if(!query_get_message_key.warn_execute())
|
||||
qdel(query_get_message_key)
|
||||
return
|
||||
@@ -479,8 +577,6 @@
|
||||
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 += ruler
|
||||
if(!isnum(index))
|
||||
index = sanitizeSQL(index)
|
||||
switch(index)
|
||||
if(1)
|
||||
search = "^."
|
||||
@@ -488,7 +584,17 @@
|
||||
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 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY targetckey")
|
||||
var/datum/db_query/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
|
||||
"}, list("search" = search))
|
||||
if(!query_list_messages.warn_execute())
|
||||
qdel(query_list_messages)
|
||||
return
|
||||
@@ -512,17 +618,24 @@
|
||||
|
||||
/proc/get_message_output(type, target_ckey)
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
if(!type)
|
||||
return
|
||||
var/output
|
||||
if(target_ckey)
|
||||
target_ckey = sanitizeSQL(target_ckey)
|
||||
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)
|
||||
var/datum/db_query/query_get_message_output = SSdbcore.NewQuery({"
|
||||
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)
|
||||
AND ((type != 'message' AND type != 'watchlist entry') OR targetckey = :targetckey)
|
||||
"}, list("targetckey" = target_ckey, "type" = type))
|
||||
if(!query_get_message_output.warn_execute())
|
||||
qdel(query_get_message_output)
|
||||
return
|
||||
@@ -536,7 +649,10 @@
|
||||
if("message")
|
||||
output += "<font color='red' size='3'><b>Admin message left by <span class='prefix'>[admin_key]</span> on [timestamp]</b></font>"
|
||||
output += "<br><font color='red'>[text]</font><br>"
|
||||
var/datum/DBQuery/query_message_read = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = [message_id]")
|
||||
var/datum/db_query/query_message_read = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = :id",
|
||||
list("id" = message_id)
|
||||
)
|
||||
if(!query_message_read.warn_execute())
|
||||
qdel(query_get_message_output)
|
||||
qdel(query_message_read)
|
||||
@@ -544,7 +660,7 @@
|
||||
qdel(query_message_read)
|
||||
if("watchlist entry")
|
||||
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(target_ckey)] has been on the watchlist since [timestamp] and has just connected - Reason: [text]</font>")
|
||||
send2irc_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
|
||||
send2tgs_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
|
||||
if("memo")
|
||||
output += "<span class='memo'>Memo by <span class='prefix'>[admin_key]</span> on [timestamp]"
|
||||
if(editor_key)
|
||||
@@ -576,7 +692,7 @@
|
||||
var/timestamp = note.group[1]
|
||||
notetext = note.group[2]
|
||||
var/admin_ckey = note.group[3]
|
||||
var/datum/DBQuery/query_convert_time = SSdbcore.NewQuery("SELECT ADDTIME(STR_TO_DATE('[timestamp]','%d-%b-%Y'), '0')")
|
||||
var/datum/db_query/query_convert_time = SSdbcore.NewQuery("SELECT ADDTIME(STR_TO_DATE(:timestamp,'%d-%b-%Y'), '0')", list("timestamp" = timestamp))
|
||||
if(!query_convert_time.Execute())
|
||||
qdel(query_convert_time)
|
||||
return
|
||||
@@ -591,7 +707,7 @@
|
||||
/*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off
|
||||
this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live
|
||||
/proc/mass_convert_notes()
|
||||
to_chat(world, "Beginning mass note conversion")
|
||||
to_chat(world, "Beginning mass note conversion", confidential = TRUE)
|
||||
var/savefile/notesfile = new(NOTESFILE)
|
||||
if(!notesfile)
|
||||
log_game("Error: Cannot access [NOTESFILE]")
|
||||
@@ -599,7 +715,7 @@ this proc can take several minutes to execute fully if converting and cause DD t
|
||||
notesfile.cd = "/"
|
||||
for(var/ckey in notesfile.dir)
|
||||
convert_notes_sql(ckey)
|
||||
to_chat(world, "Deleting NOTESFILE")
|
||||
to_chat(world, "Deleting NOTESFILE", confidential = TRUE)
|
||||
fdel(NOTESFILE)
|
||||
to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES")*/
|
||||
to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES", confidential = TRUE)*/
|
||||
#undef NOTESFILE
|
||||
|
||||
@@ -32,15 +32,11 @@
|
||||
return
|
||||
ban["message"] = "[reason]"
|
||||
|
||||
if(SSdbcore.Connect()) // todo: second wave
|
||||
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
|
||||
// INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
|
||||
// VALUES (:ckey, :message, :banning_admin)
|
||||
// "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
|
||||
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
|
||||
VALUES ([ckey], [ban["message"]], [usr.ckey])
|
||||
"})
|
||||
VALUES (:ckey, :message, :banning_admin)
|
||||
"}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
|
||||
if (query_create_stickyban.warn_execute())
|
||||
ban["fromdb"] = TRUE
|
||||
qdel(query_create_stickyban)
|
||||
@@ -74,19 +70,14 @@
|
||||
SSstickyban.cache -= ckey
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// SSdbcore.QuerySelect(list(
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
|
||||
// ), warn = TRUE, qdel = TRUE)
|
||||
SSdbcore.QuerySelect(list(
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
|
||||
), warn = TRUE, qdel = TRUE)
|
||||
|
||||
|
||||
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
|
||||
|
||||
@@ -128,12 +119,9 @@
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
|
||||
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
|
||||
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
list("ckey" = ckey, "alt" = alt)
|
||||
)
|
||||
query_remove_stickyban_alt.warn_execute()
|
||||
qdel(query_remove_stickyban_alt)
|
||||
@@ -165,12 +153,9 @@
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
|
||||
// list("reason" = reason, "ckey" = ckey)
|
||||
// )
|
||||
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
|
||||
var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
|
||||
list("reason" = reason, "ckey" = ckey)
|
||||
)
|
||||
query_edit_stickyban.warn_execute()
|
||||
qdel(query_edit_stickyban)
|
||||
@@ -218,12 +203,9 @@
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
list("ckey" = ckey, "alt" = alt)
|
||||
)
|
||||
query_exempt_stickyban_alt.warn_execute()
|
||||
qdel(query_exempt_stickyban_alt)
|
||||
@@ -271,12 +253,9 @@
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
list("ckey" = ckey, "alt" = alt)
|
||||
)
|
||||
query_unexempt_stickyban_alt.warn_execute()
|
||||
qdel(query_unexempt_stickyban_alt)
|
||||
|
||||
+14
-13
@@ -257,9 +257,8 @@
|
||||
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.ckey == banckey)
|
||||
playermob = M
|
||||
break
|
||||
|
||||
if(!playermob || M.client) // prioritise mobs with a client to stop the 'oops the dead body with no client got forwarded'
|
||||
playermob = M
|
||||
|
||||
banreason = "(MANUAL BAN) "+banreason
|
||||
|
||||
@@ -1270,8 +1269,10 @@
|
||||
else if(href_list["messageedits"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/message_id = sanitizeSQL("[href_list["messageedits"]]")
|
||||
var/datum/DBQuery/query_get_message_edits = SSdbcore.NewQuery("SELECT edits FROM [format_table_name("messages")] WHERE id = '[message_id]'")
|
||||
var/datum/db_query/query_get_message_edits = SSdbcore.NewQuery(
|
||||
"SELECT edits FROM [format_table_name("messages")] WHERE id = :message_id",
|
||||
list("message_id" = href_list["messageedits"])
|
||||
)
|
||||
if(!query_get_message_edits.warn_execute())
|
||||
qdel(query_get_message_edits)
|
||||
return
|
||||
@@ -2500,9 +2501,6 @@
|
||||
break
|
||||
return
|
||||
|
||||
else if(href_list["secrets"])
|
||||
Secrets_topic(href_list["secrets"],href_list)
|
||||
|
||||
else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
@@ -2971,16 +2969,19 @@
|
||||
to_chat(usr, "<span class='danger'>The client chosen is an admin! Cannot mentorize.</span>")
|
||||
return
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
|
||||
var/datum/db_query/query_get_mentor = SSdbcore.NewQuery(
|
||||
"SELECT id FROM [format_table_name("mentor")] WHERE ckey = :ckey",
|
||||
list("ckey" = ckey)
|
||||
)
|
||||
if(!query_get_mentor.warn_execute())
|
||||
return
|
||||
if(query_get_mentor.NextRow())
|
||||
to_chat(usr, "<span class='danger'>[ckey] is already a mentor.</span>")
|
||||
return
|
||||
var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
|
||||
var/datum/db_query/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
|
||||
if(!query_add_mentor.warn_execute())
|
||||
return
|
||||
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
|
||||
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
|
||||
if(!query_add_admin_log.warn_execute())
|
||||
return
|
||||
else
|
||||
@@ -3004,10 +3005,10 @@
|
||||
C.mentor_datum = null
|
||||
GLOB.mentors -= C
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
|
||||
var/datum/db_query/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
|
||||
if(!query_remove_mentor.warn_execute())
|
||||
return
|
||||
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');")
|
||||
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');")
|
||||
if(!query_add_admin_log.warn_execute())
|
||||
return
|
||||
else
|
||||
|
||||
@@ -205,7 +205,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
MessageNoRecipient(msg)
|
||||
|
||||
//send it to irc if nobody is on and tell us how many were on
|
||||
var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
|
||||
var/admin_number_present = send2tgs_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
|
||||
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
|
||||
if(admin_number_present <= 0)
|
||||
to_chat(C, "<span class='notice'>No active admins are online, your adminhelp was sent to the admin irc.</span>")
|
||||
@@ -222,7 +222,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
/datum/admin_help/proc/AddInteraction(formatted_message)
|
||||
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
|
||||
heard_by_no_admins = FALSE
|
||||
send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
|
||||
send2adminchat(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
|
||||
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
|
||||
|
||||
//Removes the ahelp verb and returns it after 2 minutes
|
||||
@@ -573,7 +573,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
|
||||
for(var/client/X in GLOB.admins)
|
||||
.["total"] += X
|
||||
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
|
||||
if(requiredflags != NONE && !check_rights_for(X, requiredflags))
|
||||
.["noflags"] += X
|
||||
else if(X.is_afk())
|
||||
.["afk"] += X
|
||||
@@ -582,7 +582,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
else
|
||||
.["present"] += X
|
||||
|
||||
/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
|
||||
/proc/send2tgs_adminless_only(source, msg, requiredflags = R_BAN)
|
||||
var/list/adm = get_admin_counts(requiredflags)
|
||||
var/list/activemins = adm["present"]
|
||||
. = activemins.len
|
||||
@@ -596,30 +596,52 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
final = "[msg] - No admins online"
|
||||
else
|
||||
final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
|
||||
send2irc(source,final)
|
||||
send2adminchat(source,final)
|
||||
send2otherserver(source,final)
|
||||
|
||||
|
||||
/proc/send2irc(msg,msg2)
|
||||
msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
|
||||
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
|
||||
world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
|
||||
|
||||
/proc/send2otherserver(source,msg,type = "Ahelp")
|
||||
var/comms_key = CONFIG_GET(string/comms_key)
|
||||
if(!comms_key)
|
||||
/**
|
||||
* Sends a message to a set of cross-communications-enabled servers using world topic calls
|
||||
*
|
||||
* Arguments:
|
||||
* * source - Who sent this message
|
||||
* * msg - The message body
|
||||
* * type - The type of message, becomes the topic command under the hood
|
||||
* * target_servers - A collection of servers to send the message to, defined in config
|
||||
* * additional_data - An (optional) associated list of extra parameters and data to send with this world topic call
|
||||
*/
|
||||
/proc/send2otherserver(source, msg, type = "Ahelp", target_servers, list/additional_data = list())
|
||||
if(!CONFIG_GET(string/comms_key))
|
||||
debug_world_log("Server cross-comms message not sent for lack of configured key")
|
||||
return
|
||||
var/list/message = list()
|
||||
message["message_sender"] = source
|
||||
message["message"] = msg
|
||||
message["source"] = "([CONFIG_GET(string/cross_comms_name)])"
|
||||
message["key"] = comms_key
|
||||
message += type
|
||||
|
||||
var/our_id = CONFIG_GET(string/cross_comms_name)
|
||||
additional_data["message_sender"] = source
|
||||
additional_data["message"] = msg
|
||||
additional_data["source"] = "([our_id])"
|
||||
additional_data += type
|
||||
|
||||
var/list/servers = CONFIG_GET(keyed_list/cross_server)
|
||||
for(var/I in servers)
|
||||
world.Export("[servers[I]]?[list2params(message)]")
|
||||
if(I == our_id) //No sending to ourselves
|
||||
continue
|
||||
if(target_servers && !(I in target_servers))
|
||||
continue
|
||||
world.send_cross_comms(I, additional_data)
|
||||
|
||||
/// Sends a message to a given cross comms server by name (by name for security).
|
||||
/world/proc/send_cross_comms(server_name, list/message, auth = TRUE)
|
||||
set waitfor = FALSE
|
||||
if (auth)
|
||||
var/comms_key = CONFIG_GET(string/comms_key)
|
||||
if(!comms_key)
|
||||
debug_world_log("Server cross-comms message not sent for lack of configured key")
|
||||
return
|
||||
message["key"] = comms_key
|
||||
var/list/servers = CONFIG_GET(keyed_list/cross_server)
|
||||
var/server_url = servers[server_name]
|
||||
if (!server_url)
|
||||
CRASH("Invalid cross comms config: [server_name]")
|
||||
world.Export("[server_url]?[list2params(message)]")
|
||||
|
||||
/proc/ircadminwho()
|
||||
var/list/message = list("Admins: ")
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
|
||||
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
|
||||
ircreplyamount--
|
||||
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
||||
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
||||
|
||||
else
|
||||
var/badmin = FALSE //Lets figure out if an admin is getting bwoinked.
|
||||
|
||||
@@ -308,10 +308,10 @@
|
||||
var/list/areas_with_LS = list()
|
||||
var/list/areas_with_intercom = list()
|
||||
var/list/areas_with_camera = list()
|
||||
var/list/station_areas_blacklist = typecacheof(list(/area/holodeck/rec_center, /area/shuttle, /area/engine/supermatter, /area/science/test_area, /area/space, /area/solar, /area/mine, /area/ruin, /area/asteroid))
|
||||
var/list/station_areas_blacklist = typecacheof(list(/area/holodeck/rec_center, /area/shuttle, /area/engineering/supermatter, /area/science/test_area, /area/space, /area/solars, /area/mine, /area/ruin, /area/asteroid))
|
||||
|
||||
if(SSticker.current_state == GAME_STATE_STARTUP)
|
||||
to_chat(usr, "Game still loading, please hold!")
|
||||
to_chat(usr, "Game still loading, please hold!", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/log_message
|
||||
@@ -326,8 +326,11 @@
|
||||
|
||||
for(var/area/A in world)
|
||||
if(on_station)
|
||||
var/turf/picked = safepick(get_area_turfs(A.type))
|
||||
if(picked && is_station_level(picked.z))
|
||||
var/list/area_turfs = get_area_turfs(A.type)
|
||||
if (!length(area_turfs))
|
||||
continue
|
||||
var/turf/picked = pick(area_turfs)
|
||||
if(is_station_level(picked.z))
|
||||
if(!(A.type in areas_all) && !is_type_in_typecache(A, station_areas_blacklist))
|
||||
areas_all.Add(A.type)
|
||||
else if(!(A.type in areas_all))
|
||||
@@ -790,11 +793,12 @@
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
SSmedals.hub_enabled = !SSmedals.hub_enabled
|
||||
SSachievements.achievements_enabled = !SSachievements.achievements_enabled
|
||||
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.</span>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Medal Disable") // If...
|
||||
log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.")
|
||||
log_admin("[key_name(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.")
|
||||
|
||||
|
||||
/client/proc/view_runtimes()
|
||||
set category = "Debug"
|
||||
|
||||
@@ -63,3 +63,31 @@
|
||||
load_admins()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reload All Admins") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
message_admins("[key_name_admin(usr)] manually reloaded admins")
|
||||
|
||||
/client/proc/toggle_cdn()
|
||||
set name = "Toggle CDN"
|
||||
set category = "Server"
|
||||
var/static/admin_disabled_cdn_transport = null
|
||||
if (alert(usr, "Are you sure you want to toggle the CDN asset transport?", "Confirm", "Yes", "No") != "Yes")
|
||||
return
|
||||
var/current_transport = CONFIG_GET(string/asset_transport)
|
||||
if (!current_transport || current_transport == "simple")
|
||||
if (admin_disabled_cdn_transport)
|
||||
CONFIG_SET(string/asset_transport, admin_disabled_cdn_transport)
|
||||
admin_disabled_cdn_transport = null
|
||||
SSassets.OnConfigLoad()
|
||||
message_admins("[key_name_admin(usr)] re-enabled the CDN asset transport")
|
||||
log_admin("[key_name(usr)] re-enabled the CDN asset transport")
|
||||
else
|
||||
to_chat(usr, "<span class='adminnotice'>The CDN is not enabled!</span>")
|
||||
if (alert(usr, "The CDN asset transport is not enabled! If you having issues with assets you can also try disabling filename mutations.", "The CDN asset transport is not enabled!", "Try disabling filename mutations", "Nevermind") == "Try disabling filename mutations")
|
||||
SSassets.transport.dont_mutate_filenames = !SSassets.transport.dont_mutate_filenames
|
||||
message_admins("[key_name_admin(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
|
||||
log_admin("[key_name(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
|
||||
else
|
||||
admin_disabled_cdn_transport = current_transport
|
||||
CONFIG_SET(string/asset_transport, "simple")
|
||||
SSassets.OnConfigLoad()
|
||||
SSassets.transport.dont_mutate_filenames = TRUE
|
||||
message_admins("[key_name_admin(usr)] disabled the CDN asset transport")
|
||||
log_admin("[key_name(usr)] disabled the CDN asset transport")
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
if (new_pb && !SSdbcore.Connect())
|
||||
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Panic Bunker", "[new_pb ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
send2irc("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
|
||||
send2adminchat("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
|
||||
|
||||
/client/proc/addbunkerbypass(ckeytobypass as text)
|
||||
set category = "Special Verbs"
|
||||
@@ -28,7 +28,7 @@
|
||||
SSpersistence.SavePanicBunker() //we can do this every time, it's okay
|
||||
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
|
||||
message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
|
||||
send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
|
||||
send2adminchat("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
|
||||
|
||||
/client/proc/revokebunkerbypass(ckeytobypass as text)
|
||||
set category = "Special Verbs"
|
||||
@@ -42,4 +42,4 @@
|
||||
SSpersistence.SavePanicBunker()
|
||||
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
|
||||
message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
|
||||
send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
|
||||
send2adminchat("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set name = "Pray"
|
||||
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
@@ -12,7 +12,7 @@
|
||||
log_prayer("[src.key]/([src.name]): [msg]")
|
||||
if(usr.client)
|
||||
if(usr.client.prefs.muted & MUTE_PRAY)
|
||||
to_chat(usr, "<span class='danger'>You cannot pray (muted).</span>")
|
||||
to_chat(usr, "<span class='danger'>You cannot pray (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
if(src.client.handle_spam_prevention(msg,MUTE_PRAY))
|
||||
return
|
||||
@@ -44,34 +44,37 @@
|
||||
|
||||
for(var/client/C in GLOB.admins)
|
||||
if(C.prefs.chat_toggles & CHAT_PRAYER)
|
||||
to_chat(C, msg)
|
||||
to_chat(C, msg, confidential = TRUE)
|
||||
if(C.prefs.toggles & SOUND_PRAYERS)
|
||||
if(usr.job == "Chaplain")
|
||||
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
|
||||
else
|
||||
SEND_SOUND(C, sound('sound/effects/ding.ogg'))
|
||||
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>")
|
||||
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>", confidential = TRUE)
|
||||
|
||||
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]")
|
||||
|
||||
/proc/CentCom_announce(text , mob/Sender)
|
||||
/// Used by communications consoles to message CentCom
|
||||
/proc/message_centcom(text, mob/sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>CENTCOM:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
msg = "<span class='adminnotice'><b><font color=orange>CENTCOM:</font>[ADMIN_FULLMONTY(sender)] [ADMIN_CENTCOM_REPLY(sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/console in GLOB.machines)
|
||||
console.override_cooldown()
|
||||
|
||||
/proc/Syndicate_announce(text , mob/Sender)
|
||||
/// Used by communications consoles to message the Syndicate
|
||||
/proc/message_syndicate(text, mob/sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=crimson>SYNDICATE:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
msg = "<span class='adminnotice'><b><font color=crimson>SYNDICATE:</font>[ADMIN_FULLMONTY(sender)] [ADMIN_SYNDICATE_REPLY(sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/console in GLOB.machines)
|
||||
console.override_cooldown()
|
||||
|
||||
/proc/Nuke_request(text , mob/Sender)
|
||||
/// Used by communications consoles to request the nuclear launch codes
|
||||
/proc/nuke_request(text, mob/sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>NUKE CODE REQUEST:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)] [ADMIN_SET_SD_CODE]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
msg = "<span class='adminnotice'><b><font color=orange>NUKE CODE REQUEST:</font>[ADMIN_FULLMONTY(sender)] [ADMIN_CENTCOM_REPLY(sender)] [ADMIN_SET_SD_CODE]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/console in GLOB.machines)
|
||||
console.override_cooldown()
|
||||
|
||||
@@ -1547,22 +1547,17 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
msg += "</UL></BODY></HTML>"
|
||||
src << browse(msg.Join(), "window=Player_playtime_check")
|
||||
|
||||
/datum/admins/proc/cmd_show_exp_panel(client/C)
|
||||
/datum/admins/proc/cmd_show_exp_panel(client/client_to_check)
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
if(!C)
|
||||
to_chat(usr, "<span class='danger'>ERROR: Client not found.</span>")
|
||||
if(!client_to_check)
|
||||
to_chat(usr, "<span class='danger'>ERROR: Client not found.</span>", confidential = TRUE)
|
||||
return
|
||||
if(!CONFIG_GET(flag/use_exp_tracking))
|
||||
to_chat(usr, "<span class='warning'>Tracking is disabled in the server configuration file.</span>")
|
||||
to_chat(usr, "<span class='warning'>Tracking is disabled in the server configuration file.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/body = list()
|
||||
body += "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Playtime for [C.key]</title></head><BODY><BR>Playtime:"
|
||||
body += C.get_exp_report()
|
||||
body += "<A href='?_src_=holder;[HrefToken()];toggleexempt=[REF(C)]'>Toggle Exempt status</a>"
|
||||
body += "</BODY></HTML>"
|
||||
usr << browse(body.Join(), "window=playerplaytime[C.ckey];size=550x615")
|
||||
new /datum/job_report_menu(client_to_check, usr)
|
||||
|
||||
/datum/admins/proc/toggle_exempt_status(client/C)
|
||||
if(!check_rights(R_ADMIN))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/datum/filter_editor
|
||||
var/atom/target
|
||||
|
||||
/datum/filter_editor/New(atom/target)
|
||||
src.target = target
|
||||
|
||||
/datum/filter_editor/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Filteriffic")
|
||||
ui.open()
|
||||
|
||||
/datum/filter_editor/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["filter_info"] = GLOB.master_filter_info
|
||||
return data
|
||||
|
||||
/datum/filter_editor/ui_data()
|
||||
var/list/data = list()
|
||||
data["target_name"] = target.name
|
||||
data["target_filter_data"] = target.filter_data
|
||||
return data
|
||||
|
||||
/datum/filter_editor/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("add_filter")
|
||||
var/target_name = params["name"]
|
||||
while(target.filter_data && target.filter_data[target_name])
|
||||
target_name = "[target_name]-dupe"
|
||||
target.add_filter(target_name, params["priority"], list("type" = params["type"]))
|
||||
. = TRUE
|
||||
if("remove_filter")
|
||||
target.remove_filter(params["name"])
|
||||
. = TRUE
|
||||
if("rename_filter")
|
||||
var/list/filter_data = target.filter_data[params["name"]]
|
||||
target.remove_filter(params["name"])
|
||||
target.add_filter(params["new_name"], filter_data["priority"], filter_data)
|
||||
. = TRUE
|
||||
if("edit_filter")
|
||||
target.remove_filter(params["name"])
|
||||
target.add_filter(params["name"], params["priority"], params["new_filter"])
|
||||
. = TRUE
|
||||
if("change_priority")
|
||||
var/new_priority = params["new_priority"]
|
||||
target.change_filter_priority(params["name"], new_priority)
|
||||
. = TRUE
|
||||
if("transition_filter_value")
|
||||
target.transition_filter(params["name"], 4, params["new_data"])
|
||||
. = TRUE
|
||||
if("modify_filter_value")
|
||||
var/list/old_filter_data = target.filter_data[params["name"]]
|
||||
var/list/new_filter_data = old_filter_data.Copy()
|
||||
for(var/entry in params["new_data"])
|
||||
new_filter_data[entry] = params["new_data"][entry]
|
||||
for(var/entry in new_filter_data)
|
||||
if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry])
|
||||
new_filter_data.Remove(entry)
|
||||
target.remove_filter(params["name"])
|
||||
target.add_filter(params["name"], old_filter_data["priority"], new_filter_data)
|
||||
. = TRUE
|
||||
if("modify_color_value")
|
||||
var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null
|
||||
if(new_color)
|
||||
target.transition_filter(params["name"], 4, list("color" = new_color))
|
||||
. = TRUE
|
||||
if("modify_icon_value")
|
||||
var/icon/new_icon = input("Pick icon:", "Icon") as null|icon
|
||||
if(new_icon)
|
||||
target.filter_data[params["name"]]["icon"] = new_icon
|
||||
target.update_filters()
|
||||
. = TRUE
|
||||
if("mass_apply")
|
||||
if(!check_rights_for(usr.client, R_FUN))
|
||||
to_chat(usr, "<span class='userdanger>Stay in your lane, jannie.</span>'")
|
||||
return
|
||||
var/target_path = text2path(params["path"])
|
||||
if(!target_path)
|
||||
return
|
||||
var/filters_to_copy = target.filters
|
||||
var/filter_data_to_copy = target.filter_data
|
||||
var/count = 0
|
||||
for(var/thing in world.contents)
|
||||
if(istype(thing, target_path))
|
||||
var/atom/thing_at = thing
|
||||
thing_at.filters = filters_to_copy
|
||||
thing_at.filter_data = filter_data_to_copy
|
||||
count += 1
|
||||
message_admins("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")
|
||||
log_admin("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
|
||||
var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)
|
||||
|
||||
if(tgui_alert(src, "Are you sure you'd like to mass-modify every instance of the [var_name] variable? This can break everything if you do not know what you are doing.", "Slow down, chief!", list("Yes", "No")) != "Yes")
|
||||
return
|
||||
|
||||
if(!check_rights(R_VAREDIT))
|
||||
return
|
||||
|
||||
if(A && A.type)
|
||||
if(A?.type)
|
||||
method = vv_subtype_prompt(A.type)
|
||||
|
||||
src.massmodify_variables(A, var_name, method)
|
||||
@@ -114,7 +117,7 @@
|
||||
var/list/varsvars = vv_parse_text(O, new_value)
|
||||
var/pre_processing = new_value
|
||||
var/unique
|
||||
if (varsvars && varsvars.len)
|
||||
if (varsvars?.len)
|
||||
unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same")
|
||||
if(unique == "Unique")
|
||||
unique = TRUE
|
||||
|
||||
@@ -23,7 +23,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
var/list/subtypes = subtypesof(type)
|
||||
if (!subtypes || !subtypes.len)
|
||||
return FALSE
|
||||
if (subtypes && subtypes.len)
|
||||
if (subtypes?.len)
|
||||
switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel"))
|
||||
if("Strictly this type")
|
||||
return FALSE
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
return
|
||||
|
||||
var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN)
|
||||
|
||||
// If the new name is something that would be restricted by IC chat filters,
|
||||
// give the admin a warning but allow them to do it anyway if they want.
|
||||
// if(CHAT_FILTER_CHECK(new_name) && alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", "Confirm", "Cancel") == "Cancel")
|
||||
// return
|
||||
|
||||
if( !new_name || !M )
|
||||
return
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
usr.client.debug_variables(src)
|
||||
return
|
||||
|
||||
#ifdef REFERENCE_TRACKING //people with debug can only access this putnam!
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(href_list[VV_HK_VIEW_REFERENCES])
|
||||
var/datum/D = locate(href_list[VV_HK_TARGET])
|
||||
if(!D)
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
if(!D)
|
||||
return
|
||||
|
||||
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/simple/vv)
|
||||
asset_cache_datum.send(usr)
|
||||
|
||||
var/islist = islist(D)
|
||||
if(!islist && !istype(D))
|
||||
return
|
||||
|
||||
@@ -88,7 +88,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
|
||||
/mob/camera/blob/proc/is_valid_turf(turf/T)
|
||||
var/area/A = get_area(T)
|
||||
if((A && !A.blob_allowed) || !T || !is_station_level(T.z) || isspaceturf(T))
|
||||
if((A && !(A.area_flags & BLOBS_ALLOWED)) || !T || !is_station_level(T.z) || isspaceturf(T))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -131,7 +131,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
|
||||
var/area/Ablob = get_area(T)
|
||||
|
||||
if(!Ablob.blob_allowed)
|
||||
if(!(Ablob.area_flags & BLOBS_ALLOWED))
|
||||
continue
|
||||
|
||||
if(!(ROLE_BLOB in L.faction))
|
||||
@@ -144,7 +144,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
for(var/area/A in GLOB.sortedAreas)
|
||||
if(!(A.type in GLOB.the_station_areas))
|
||||
continue
|
||||
if(!A.blob_allowed)
|
||||
if(!(A.area_flags & BLOBS_ALLOWED))
|
||||
continue
|
||||
A.color = blobstrain.color
|
||||
A.name = "blob"
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
to_chat(src, "<span class='userdanger'>You have no core and are about to die! May you rest in peace.</span>")
|
||||
return
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || A && !A.blob_allowed)
|
||||
if(isspaceturf(T) || A && !(A.area_flags & BLOBS_ALLOWED))
|
||||
to_chat(src, "<span class='warning'>You cannot relocate your core here!</span>")
|
||||
return
|
||||
if(!can_buy(80))
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
if(owner_overmind)
|
||||
overmind = owner_overmind
|
||||
var/area/Ablob = get_area(src)
|
||||
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
|
||||
if(Ablob.area_flags & BLOBS_ALLOWED) //Is this area allowed for winning as blob?
|
||||
overmind.blobs_legit += src
|
||||
GLOB.blobs += src //Keep track of the blob in the normal list either way
|
||||
setDir(pick(GLOB.cardinals))
|
||||
|
||||
@@ -7,98 +7,79 @@
|
||||
// Over Time, tick down toward a "Solar Flare" of UV buffeting the station. This period is harmful to vamps.
|
||||
/obj/effect/sunlight
|
||||
//var/amDay = FALSE
|
||||
var/cancel_me = FALSE
|
||||
var/amDay = FALSE
|
||||
var/time_til_cycle = 0
|
||||
var/nightime_duration = 900 //15 Minutes
|
||||
var/nighttime_duration = 900 //15 Minutes
|
||||
var/issued_XP = FALSE
|
||||
|
||||
/obj/effect/sunlight/Initialize()
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/countdown)
|
||||
INVOKE_ASYNC(src, .proc/hud_tick)
|
||||
|
||||
/obj/effect/sunlight/proc/countdown()
|
||||
set waitfor = FALSE
|
||||
/obj/effect/sunlight/proc/start_countdown()
|
||||
START_PROCESSING(SSweather, src) //it counts as weather right
|
||||
time_til_cycle = nighttime_duration
|
||||
|
||||
while(!cancel_me)
|
||||
|
||||
time_til_cycle = nightime_duration
|
||||
|
||||
// Part 1: Night (all is well)
|
||||
while(time_til_cycle > TIME_BLOODSUCKER_DAY_WARN)
|
||||
sleep(10)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
|
||||
warn_daylight(1,"<span class = 'danger'>Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. <b>Prepare to seek cover in a coffin or closet.</b></span>") // time2text <-- use Help On
|
||||
give_home_power() // Give VANISHING ACT power to all vamps with a lair!
|
||||
|
||||
// Part 2: Night Ending
|
||||
while(time_til_cycle > TIME_BLOODSUCKER_DAY_FINAL_WARN)
|
||||
sleep(10)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(TIME_BLOODSUCKER_DAY_WARN - TIME_BLOODSUCKER_DAY_FINAL_WARN)
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
|
||||
warn_daylight(2,"<span class = 'userdanger'>Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!</span>",\
|
||||
"<span class = 'danger'>In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!</span>")
|
||||
|
||||
// (FINAL LIL WARNING)
|
||||
while(time_til_cycle > 5)
|
||||
sleep(10)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
|
||||
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
|
||||
|
||||
// Part 3: Night Ending
|
||||
while(time_til_cycle > 0)
|
||||
sleep(10)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(50)
|
||||
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
|
||||
"<span class = 'danger'>Solar flares bombard the station with UV light!</span>")
|
||||
|
||||
// Part 4: Day
|
||||
amDay = TRUE
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
|
||||
time_til_cycle = TIME_BLOODSUCKER_DAY
|
||||
sleep(10) // One second grace period.
|
||||
//var/daylight_time = TIME_BLOODSUCKER_DAY
|
||||
var/issued_XP = FALSE
|
||||
while(time_til_cycle > 0)
|
||||
/obj/effect/sunlight/process()
|
||||
// Update all Bloodsucker sunlight huds
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
if(!istype(M) || !istype(M.current))
|
||||
continue
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(istype(bloodsuckerdatum))
|
||||
bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs
|
||||
time_til_cycle--
|
||||
if(amDay)
|
||||
if(time_til_cycle > 0 && time_til_cycle % 4 == 0)
|
||||
punish_vamps()
|
||||
sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
|
||||
if(cancel_me)
|
||||
return
|
||||
//daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
|
||||
// Issue Level Up!
|
||||
if(!issued_XP && time_til_cycle <= 5)
|
||||
issued_XP = TRUE
|
||||
vamps_rank_up()
|
||||
// Cycle through all vamp antags and check if they're inside a closet.
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
if(!istype(M) || !istype(M.current))
|
||||
continue
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(istype(bloodsuckerdatum))
|
||||
bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
|
||||
|
||||
warn_daylight(5,"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>",\
|
||||
"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>")
|
||||
amDay = FALSE
|
||||
day_end() // Remove VANISHING ACT power from all vamps who have it! Clear Warnings (sunlight, locker protection)
|
||||
nightime_duration += 100 //Each day makes the night a minute longer.
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nightime_duration / 60] minutes.)")
|
||||
|
||||
|
||||
|
||||
/obj/effect/sunlight/proc/hud_tick()
|
||||
set waitfor = FALSE
|
||||
while(!cancel_me)
|
||||
// Update all Bloodsucker sunlight huds
|
||||
issued_XP = FALSE
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
if(!istype(M) || !istype(M.current))
|
||||
continue
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(istype(bloodsuckerdatum))
|
||||
bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs
|
||||
sleep(10)
|
||||
time_til_cycle --
|
||||
if(!istype(bloodsuckerdatum))
|
||||
continue
|
||||
// Reset Warnings
|
||||
bloodsuckerdatum.warn_sun_locker = FALSE
|
||||
bloodsuckerdatum.warn_sun_burn = FALSE
|
||||
// Remove Dawn Powers
|
||||
for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
|
||||
if(istype(P, /datum/action/bloodsucker/gohome))
|
||||
bloodsuckerdatum.powers -= P
|
||||
P.Remove(M.current)
|
||||
nighttime_duration += 100 //Each day makes the night a minute longer.
|
||||
time_til_cycle = nighttime_duration
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nighttime_duration / 60] minutes.)")
|
||||
else
|
||||
switch(time_til_cycle)
|
||||
if(TIME_BLOODSUCKER_DAY_WARN)
|
||||
//sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
|
||||
warn_daylight(1,"<span class = 'danger'>Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. <b>Prepare to seek cover in a coffin or closet.</b></span>") // time2text <-- use Help On
|
||||
give_home_power() // Give VANISHING ACT power to all vamps with a lair!
|
||||
if(TIME_BLOODSUCKER_DAY_FINAL_WARN)
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
|
||||
warn_daylight(2,"<span class = 'userdanger'>Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!</span>",\
|
||||
"<span class = 'danger'>In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!</span>")
|
||||
if(5)
|
||||
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
|
||||
if(0)
|
||||
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
|
||||
"<span class = 'danger'>Solar flares bombard the station with UV light!</span>")
|
||||
amDay = TRUE
|
||||
message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
|
||||
time_til_cycle = TIME_BLOODSUCKER_DAY
|
||||
|
||||
/obj/effect/sunlight/proc/warn_daylight(danger_level =0, vampwarn = "", vassalwarn = "")
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
@@ -162,32 +143,6 @@
|
||||
M.current.updatehealth()
|
||||
SEND_SIGNAL(M.current, COMSIG_ADD_MOOD_EVENT, "vampsleep", /datum/mood_event/daylight_2)
|
||||
|
||||
/obj/effect/sunlight/proc/day_end()
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
if(!istype(M) || !istype(M.current))
|
||||
continue
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(!istype(bloodsuckerdatum))
|
||||
continue
|
||||
// Reset Warnings
|
||||
bloodsuckerdatum.warn_sun_locker = FALSE
|
||||
bloodsuckerdatum.warn_sun_burn = FALSE
|
||||
// Remove Dawn Powers
|
||||
for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
|
||||
if(istype(P, /datum/action/bloodsucker/gohome))
|
||||
bloodsuckerdatum.powers -= P
|
||||
P.Remove(M.current)
|
||||
|
||||
/obj/effect/sunlight/proc/vamps_rank_up()
|
||||
set waitfor = FALSE
|
||||
// Cycle through all vamp antags and check if they're inside a closet.
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
if(!istype(M) || !istype(M.current))
|
||||
continue
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(istype(bloodsuckerdatum))
|
||||
bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
|
||||
|
||||
/obj/effect/sunlight/proc/give_home_power()
|
||||
// It's late...! Give the "Vanishing Act" gohome power to bloodsuckers.
|
||||
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
|
||||
|
||||
@@ -43,14 +43,13 @@
|
||||
// This exists so Hardened/Silver Stake can't have a welding torch used on them.
|
||||
|
||||
/obj/item/stake/basic/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/weldingtool))
|
||||
if(W.tool_behaviour == TOOL_WELDER)
|
||||
//if (amWelded)
|
||||
// to_chat(user, "<span class='warning'>This stake has already been treated with fire.</span>")
|
||||
// return
|
||||
//amWelded = TRUE
|
||||
// Weld it
|
||||
var/obj/item/weldingtool/WT = W
|
||||
if(WT.use(0))//remove_fuel(0,user))
|
||||
if(W.use(0))//remove_fuel(0,user))
|
||||
user.visible_message("[user.name] scorched the pointy end of [src] with the welding tool.", \
|
||||
"<span class='notice'>You scorch the pointy end of [src] with the welding tool.</span>", \
|
||||
"<span class='italics'>You hear welding.</span>")
|
||||
|
||||
@@ -164,11 +164,11 @@
|
||||
if(istype(W, cutting_tool))
|
||||
to_chat(user, "<span class='notice'>This is a much more complex mechanical structure than you thought. You don't know where to begin cutting [src].</span>")
|
||||
return
|
||||
else if(anchored && istype(W, /obj/item/wrench)) // Can't unanchor unless owner.
|
||||
else if(anchored && W.tool_behaviour == TOOL_WRENCH) // Can't unanchor unless owner.
|
||||
to_chat(user, "<span class='danger'>The coffin won't come unanchored from the floor.</span>")
|
||||
return
|
||||
|
||||
if(locked && istype(W, /obj/item/crowbar))
|
||||
if(locked && W.tool_behaviour == TOOL_CROWBAR)
|
||||
var/pry_time = pryLidTimer * W.toolspeed // Pry speed must be affected by the speed of the tool.
|
||||
user.visible_message("<span class='notice'>[user] tries to pry the lid off of [src] with [W].</span>", \
|
||||
"<span class='notice'>You begin prying the lid off of [src] with [W]. This should take about [DisplayTimeText(pry_time)].</span>")
|
||||
|
||||
@@ -149,8 +149,6 @@
|
||||
if(prob(50))
|
||||
if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
|
||||
add_objective(new/datum/objective/destroy, TRUE)
|
||||
else if(prob(30))
|
||||
add_objective(new/datum/objective/maroon, TRUE)
|
||||
else
|
||||
add_objective(new/datum/objective/assassinate, TRUE)
|
||||
else
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
/datum/antagonist/changeling/proc/remove_changeling_powers()
|
||||
if(ishuman(owner.current) || ismonkey(owner.current))
|
||||
reset_properties()
|
||||
QDEL_NULL(cellular_emporium)
|
||||
QDEL_NULL(emporium_action)
|
||||
for(var/obj/effect/proc_holder/changeling/p in purchasedpowers)
|
||||
if(p.always_keep)
|
||||
continue
|
||||
@@ -139,6 +141,7 @@
|
||||
/datum/antagonist/changeling/proc/reset_powers()
|
||||
if(purchasedpowers)
|
||||
remove_changeling_powers()
|
||||
create_actions()
|
||||
//Repurchase free powers.
|
||||
for(var/path in all_powers)
|
||||
var/obj/effect/proc_holder/changeling/S = new path()
|
||||
@@ -225,7 +228,8 @@
|
||||
/datum/antagonist/changeling/proc/regenerate()
|
||||
var/mob/living/carbon/the_ling = owner.current
|
||||
if(istype(the_ling))
|
||||
emporium_action.Grant(the_ling)
|
||||
if(emporium_action)
|
||||
emporium_action.Grant(the_ling)
|
||||
if(the_ling.stat == DEAD)
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5))
|
||||
geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1)
|
||||
@@ -433,30 +437,21 @@
|
||||
destroy_objective.find_target()
|
||||
objectives += destroy_objective
|
||||
else
|
||||
if(prob(70))
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
if(team_mode) //No backstabbing while in a team
|
||||
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
kill_objective.find_target()
|
||||
objectives += kill_objective
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
if(team_mode) //No backstabbing while in a team
|
||||
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
if(team_mode)
|
||||
maroon_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
maroon_objective.find_target()
|
||||
objectives += maroon_objective
|
||||
kill_objective.find_target()
|
||||
objectives += kill_objective
|
||||
|
||||
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = owner
|
||||
identity_theft.target = maroon_objective.target
|
||||
identity_theft.update_explanation_text()
|
||||
objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
if(!(locate(/datum/objective/escape) in objectives) && escape_objective_possible && prob(50))
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = owner
|
||||
identity_theft.target = kill_objective.target
|
||||
identity_theft.update_explanation_text()
|
||||
objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
|
||||
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
|
||||
if(prob(50))
|
||||
|
||||
@@ -12,24 +12,34 @@
|
||||
|
||||
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
|
||||
var/used = FALSE // only one form of shackles removed per use
|
||||
if(!user.restrained() && isopenturf(user.loc))
|
||||
if(!user.restrained() && !user.legcuffed && isopenturf(user.loc))
|
||||
to_chat(user, "<span class='warning'>We are already free!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(user.handcuffed)
|
||||
var/obj/O = user.get_item_by_slot(SLOT_HANDCUFFED)
|
||||
if(!istype(O))
|
||||
return 0
|
||||
return FALSE
|
||||
user.visible_message("<span class='warning'>[user] vomits a glob of acid on [user.p_their()] [O]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our restraints!</span>")
|
||||
|
||||
addtimer(CALLBACK(src, .proc/dissolve_handcuffs, user, O), 30)
|
||||
used = TRUE
|
||||
|
||||
if(user.legcuffed)
|
||||
var/obj/O = user.get_item_by_slot(SLOT_LEGCUFFED)
|
||||
if(!istype(O))
|
||||
return FALSE
|
||||
user.visible_message("<span class='warning'>[user] vomits a glob of acid on [user.p_their()] [O]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our restraints!</span>")
|
||||
|
||||
addtimer(CALLBACK(src, .proc/dissolve_legcuffs, user, O), 30)
|
||||
used = TRUE
|
||||
|
||||
if(user.wear_suit && user.wear_suit.breakouttime && !used)
|
||||
var/obj/item/clothing/suit/S = user.get_item_by_slot(SLOT_WEAR_SUIT)
|
||||
if(!istype(S))
|
||||
return 0
|
||||
return FALSE
|
||||
user.visible_message("<span class='warning'>[user] vomits a glob of acid across the front of [user.p_their()] [S]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our straight jacket!</span>")
|
||||
addtimer(CALLBACK(src, .proc/dissolve_straightjacket, user, S), 30)
|
||||
@@ -39,7 +49,7 @@
|
||||
if(istype(user.loc, /obj/structure/closet) && !used)
|
||||
var/obj/structure/closet/C = user.loc
|
||||
if(!istype(C))
|
||||
return 0
|
||||
return FALSE
|
||||
C.visible_message("<span class='warning'>[C]'s hinges suddenly begin to melt and run!</span>")
|
||||
to_chat(user, "<span class='warning'>We vomit acidic goop onto the interior of [C]!</span>")
|
||||
addtimer(CALLBACK(src, .proc/open_closet, user, C), 70)
|
||||
@@ -48,7 +58,7 @@
|
||||
if(istype(user.loc, /obj/structure/spider/cocoon) && !used)
|
||||
var/obj/structure/spider/cocoon/C = user.loc
|
||||
if(!istype(C))
|
||||
return 0
|
||||
return FALSE
|
||||
C.visible_message("<span class='warning'>[src] shifts and starts to fall apart!</span>")
|
||||
to_chat(user, "<span class='warning'>We secrete acidic enzymes from our skin and begin melting our cocoon...</span>")
|
||||
addtimer(CALLBACK(src, .proc/dissolve_cocoon, user, C), 25) //Very short because it's just webs
|
||||
@@ -62,6 +72,12 @@
|
||||
new /obj/effect/decal/cleanable/greenglow(O.drop_location())
|
||||
qdel(O)
|
||||
|
||||
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_legcuffs(mob/living/carbon/human/user, obj/O)
|
||||
if(O && user.legcuffed == O)
|
||||
user.visible_message("<span class='warning'>[O] dissolve[O.gender==PLURAL?"":"s"] into a puddle of sizzling goop.</span>")
|
||||
new /obj/effect/decal/cleanable/greenglow(O.drop_location())
|
||||
qdel(O)
|
||||
|
||||
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_straightjacket(mob/living/carbon/human/user, obj/S)
|
||||
if(S && user.wear_suit == S)
|
||||
user.visible_message("<span class='warning'>[S] dissolves into a puddle of sizzling goop.</span>")
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
|
||||
//Revive from revival stasis
|
||||
/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user)
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
if(!changeling)
|
||||
return
|
||||
if(changeling.hostile_absorbed)
|
||||
to_chat(user, "<span class='notice'>We cannot muster up the strength to revive ourselves!</span>")
|
||||
changeling.purchasedpowers -= src
|
||||
src.action.Remove(user)
|
||||
return
|
||||
user.cure_fakedeath("changeling")
|
||||
user.revive(full_heal = 1)
|
||||
var/list/missing = user.get_missing_limbs()
|
||||
@@ -27,7 +35,6 @@
|
||||
user.regenerate_limbs(0, list(BODY_ZONE_HEAD))
|
||||
user.regenerate_organs()
|
||||
to_chat(user, "<span class='notice'>We have revived ourselves.</span>")
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
changeling.purchasedpowers -= src
|
||||
src.action.Remove(user)
|
||||
return TRUE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/obj/effect/proc_holder/changeling/strained_muscles
|
||||
name = "Strained Muscles"
|
||||
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
|
||||
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
|
||||
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Our chemical generation is drastically slowed while this is active. Cannot be used in lesser form."
|
||||
dna_cost = 1
|
||||
req_human = 1
|
||||
var/stacks = 0 //Increments every 5 seconds; damage increases over time
|
||||
@@ -14,12 +14,15 @@
|
||||
action_background_icon_state = "bg_ling"
|
||||
|
||||
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
active = !active
|
||||
if(active)
|
||||
to_chat(user, "<span class='notice'>Our muscles tense and strengthen.</span>")
|
||||
changeling.chem_recharge_slowdown += 0.8 // stacking this with other abilities will cause you to actively lose chemicals
|
||||
else
|
||||
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
|
||||
changeling.chem_recharge_slowdown -= 0.8
|
||||
if(stacks >= 10)
|
||||
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
|
||||
user.DefaultCombatKnockdown(60)
|
||||
@@ -30,6 +33,7 @@
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user)
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
while(active)
|
||||
user.add_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
|
||||
@@ -37,6 +41,7 @@
|
||||
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
|
||||
user.DefaultCombatKnockdown(40)
|
||||
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
changeling.chem_recharge_slowdown -= 0.8
|
||||
break
|
||||
|
||||
stacks++
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
to_chat(L, "<span class='heavy_brass'>\"You belong to me now.\"</span>")
|
||||
if(!GLOB.application_scripture_unlocked)
|
||||
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>")
|
||||
hierophant_message("<span class='large_brass bold'>With the conversion of a new servant the Hierophant Network's power grows. Application scriptures are now available.</span>")
|
||||
if(add_servant_of_ratvar(L))
|
||||
L.log_message("conversion was done with a [sigil_name]", LOG_ATTACK, color="BE8700")
|
||||
if(iscarbon(L))
|
||||
|
||||
@@ -196,4 +196,68 @@
|
||||
CL.visible_message("<span class='warning'>[CL] materialises out of thin air!")
|
||||
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
|
||||
|
||||
//summons a soul vessel, which is the clockwork cult version of a soul shard. It acts like a posibrain and, as long as the target has a brain, a soul shard.
|
||||
/datum/clockwork_rite/soul_vessel
|
||||
name = "Rite of the Vessel" //The name of the rite
|
||||
desc = "This rite is used to summon a soul vessel, a special posibrain that makes whoever has their brain put into it loyal to the Justiciar.,\
|
||||
When put into a cyborg shell, the created cyborg will automatically be a servant of Ratvar."
|
||||
required_ingredients = list(/obj/item/stack/cable_coil, /obj/item/stock_parts/cell/, /obj/item/organ/cyberimp)
|
||||
power_cost = 2500 //These things are pretty strong, I won't lie
|
||||
requires_full_power = TRUE
|
||||
cast_time = 50
|
||||
limit = INFINITE
|
||||
rite_cast_sound = 'sound/magic/summon_guns.ogg'
|
||||
|
||||
/datum/clockwork_rite/soul_vessel/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
var/obj/item/mmi/posibrain/soul_vessel/SV = new /obj/item/mmi/posibrain/soul_vessel(T)
|
||||
SV.visible_message("<span class='warning'>[SV] materalizes out of thin air!</span>")
|
||||
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
|
||||
|
||||
|
||||
/datum/clockwork_rite/cyborg_transform
|
||||
name = "Rite of the Divine Form"
|
||||
desc = "This rite is used to ascend into a cyborg, gaining unique scripture and a loadout that depends on which module is chosen. Consult the wiki for details on each cyborg module's loadout. Mutually exclusive to Enhanced Form."
|
||||
required_ingredients = list(/obj/item/mmi/posibrain, /obj/item/stack/cable_coil, /obj/item/stock_parts/cell/super, /obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/l_leg/robot)
|
||||
power_cost = 20000
|
||||
requires_human = TRUE
|
||||
requires_full_power = FALSE
|
||||
cast_time = 100
|
||||
limit = INFINITE
|
||||
rite_cast_sound = 'sound/magic/disable_tech.ogg'
|
||||
|
||||
/datum/clockwork_rite/cyborg_transform/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(isclockworkgolem(target))
|
||||
return FALSE
|
||||
target.visible_message("<span class='warning'>The robotic parts magnetize to [target], the new frame's eyes glowing in a brilliant yellow!</span>")
|
||||
var/mob/living/silicon/robot/R = target.Robotize()
|
||||
R.cell = new /obj/item/stock_parts/cell/super(R)//takes one to use the rite to begin with
|
||||
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
|
||||
|
||||
/datum/clockwork_rite/golem_transform
|
||||
name = "Rite of the Enhanced Form"
|
||||
desc = "This rite is used to shed one's flesh to become a clockwork automaton, becoming immune to many environmental hazards as well as being more resilient to incoming damage. Mutually exclusive to Divine Form."
|
||||
required_ingredients = list(/obj/item/mmi/posibrain, /obj/item/stock_parts/cell/super, /obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/l_leg/robot)
|
||||
power_cost = 20000
|
||||
requires_human = TRUE
|
||||
requires_full_power = FALSE
|
||||
cast_time = 100
|
||||
limit = INFINITE
|
||||
rite_cast_sound = 'sound/magic/disable_tech.ogg'
|
||||
|
||||
|
||||
/datum/clockwork_rite/golem_transform/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
target.visible_message("<span class='warning'>The robotic parts magnetize to [target], the humanoid shape's eye glowing with an inner flame!</span>")
|
||||
to_chat(target, "<span class='bold alloy'>The rite's power warps your body into a clockwork form! You are now immune to many hazards, and your body is more robust against damage!</span>")
|
||||
target.set_species(/datum/species/golem/clockwork/no_scrap)
|
||||
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
|
||||
|
||||
#undef INFINITE
|
||||
|
||||
@@ -350,3 +350,22 @@
|
||||
// Winter coat
|
||||
/obj/item/clothing/suit/hooded/wintercoat/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //four sheets of metal
|
||||
return list("operation_time" = 30, "new_obj_type" = /obj/item/clothing/suit/hooded/wintercoat/ratvar, "power_cost" = POWER_METAL * 4, "spawn_dir" = SOUTH)
|
||||
|
||||
//tools
|
||||
/obj/item/crowbar/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //1 metal rod each
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/crowbar/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/screwdriver/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/screwdriver/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/weldingtool/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/weldingtool/experimental/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/wirecutters/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/wirecutters/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/wrench/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/wrench/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/multitool/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
|
||||
return list("operation_time" = 2, "new_obj_type" = /obj/item/multitool/advanced/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
update_slab_info()
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(is_servant_of_ratvar(M) || isobserver(M))
|
||||
M.playsound_local(M, 'sound/magic/clockwork/scripture_tier_up.ogg', 50, FALSE, pressure_affected = FALSE)
|
||||
M.playsound_local(M, 'sound/magic/clockwork/scripture_tier_up.ogg', 20, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/proc/update_slab_info(obj/item/clockwork/slab/set_slab)
|
||||
generate_all_scripture()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
name = "clock-themed arm-mounted implant"
|
||||
var/clockwork_desc = "According to Ratvar, this really shouldn't exist. Tell Him about this immediately."
|
||||
syndicate_implant = TRUE
|
||||
icon_state = "clock_arm_implant"
|
||||
icon_state = "toolkit_implant"
|
||||
|
||||
/obj/item/organ/cyberimp/arm/clockwork/ui_action_click()
|
||||
if(is_servant_of_ratvar(owner) || (obj_flags & EMAGGED)) //If you somehow manage to steal a clockie's implant AND have an emag AND manage to get it implanted for yourself, good on ya!
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
desc = "A resilient shield made out of brass.. It feels warm to the touch."
|
||||
var/clockwork_desc = "A powerful shield of ratvarian making. It absorbs blocked attacks to charge devastating bashes."
|
||||
armor = list("melee" = 80, "bullet" = 70, "laser" = -10, "energy" = -20, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
|
||||
shield_flags = SHIELD_FLAGS_DEFAULT
|
||||
shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_KINETIC_STRONG | SHIELD_ENERGY_WEAK
|
||||
max_integrity = 300 //High integrity, extremely strong against melee / bullets, but still quite easy to destroy with lasers and energy
|
||||
repair_material = /obj/item/stack/tile/brass
|
||||
var/dam_absorbed = 0
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
var/recollecting = TRUE //if we're looking at fancy recollection. tutorial enabled by default
|
||||
var/recollection_category = "Default"
|
||||
|
||||
var/list/quickbound = list(/datum/clockwork_scripture/spatial_gateway, \
|
||||
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
|
||||
var/list/quickbound = list(
|
||||
/datum/clockwork_scripture/spatial_gateway,
|
||||
/datum/clockwork_scripture/ranged_ability/kindle,
|
||||
/datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
|
||||
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
|
||||
|
||||
var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing
|
||||
@@ -326,6 +328,7 @@
|
||||
"requirement" = "Unlock powerful equipment and structures by converting five servants or if [DisplayPower(JUDGEMENT_UNLOCK_THRESHOLD)] of power is reached..",
|
||||
"ready" = SSticker.scripture_states[SCRIPTURE_JUDGEMENT]
|
||||
)
|
||||
// no need to learn shit, ratvar is free
|
||||
.["recollection_categories"] = list()
|
||||
if(GLOB.ratvar_awakens)
|
||||
return
|
||||
@@ -340,19 +343,25 @@
|
||||
)
|
||||
.["rec_section"] = get_recollection(recollection_category)
|
||||
generate_all_scripture()
|
||||
//needs a new place to live, preferably when clockcult unlocks/downgrades a tier. Smart enough to earlyreturn.
|
||||
//needs a new place to live, preferably when clockcult unlocks/downgrades a tier.
|
||||
//comsig maybe?
|
||||
|
||||
/obj/item/clockwork/slab/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("toggle")
|
||||
recollecting = !recollecting
|
||||
. = TRUE
|
||||
if("recite")
|
||||
INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["script"]), usr, FALSE)
|
||||
. = TRUE
|
||||
if("bind")
|
||||
var/datum/clockwork_scripture/path = text2path(params["script"]) //we need a path and not a string
|
||||
if(!ispath(path, /datum/clockwork_scripture) || !initial(path.quickbind) || initial(path.tier) == SCRIPTURE_PERIPHERAL) //fuck you href bus
|
||||
to_chat(usr, "<span class='warning'>Nice try using href exploits</span>")
|
||||
return
|
||||
return FALSE
|
||||
var/found_index = quickbound.Find(path)
|
||||
if(found_index) //hey, we already HAVE this bound
|
||||
if(LAZYLEN(quickbound) == found_index) //if it's the last scripture, remove it instead of leaving a null
|
||||
@@ -361,6 +370,7 @@
|
||||
quickbound[found_index] = null //otherwise, leave it as a null so the scripture maintains position
|
||||
update_quickbind()
|
||||
else
|
||||
// todo: async this due to ((input)) but its fine for now
|
||||
var/target_index = input("Position of [initial(path.name)], 1 to [maximum_quickbound]?", "Input") as num|null
|
||||
if(isnum(target_index) && target_index > 0 && target_index <= maximum_quickbound && !..())
|
||||
var/datum/clockwork_scripture/S
|
||||
@@ -368,10 +378,11 @@
|
||||
S = quickbound[target_index]
|
||||
if(S != path)
|
||||
quickbind_to_slot(path, target_index)
|
||||
. = TRUE
|
||||
if("rec_category")
|
||||
recollection_category = params["category"]
|
||||
update_static_data()
|
||||
return TRUE
|
||||
. = TRUE
|
||||
|
||||
/obj/item/clockwork/slab/proc/quickbind_to_slot(datum/clockwork_scripture/scripture, index) //takes a typepath(typecast for initial()) and binds it to a slot
|
||||
if(!ispath(scripture) || !scripture || (scripture in quickbound))
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
lastWarning = world.time
|
||||
to_chat(src, "<span class='warning'>This turf is consecrated and can't be crossed!</span>")
|
||||
return
|
||||
if(istype(get_area(T), /area/chapel))
|
||||
if(istype(get_area(T), /area/service/chapel))
|
||||
if((world.time - lastWarning) >= 30)
|
||||
lastWarning = world.time
|
||||
to_chat(src, "<span class='warning'>The Chapel is hallowed ground under a heretical deity, and can't be accessed!</span>")
|
||||
@@ -251,7 +251,9 @@
|
||||
var/mob/camera/eminence/E = owner
|
||||
E.eminence_help()
|
||||
|
||||
|
||||
/*
|
||||
|
||||
//Returns to the Ark - Commented out and replaced with obelisk_jump
|
||||
/datum/action/innate/eminence/ark_jump
|
||||
name = "Return to Ark"
|
||||
@@ -265,7 +267,7 @@
|
||||
owner.playsound_local(owner, 'sound/magic/magic_missile.ogg', 50, TRUE)
|
||||
flash_color(owner, flash_color = "#AF0AAF", flash_time = 25)
|
||||
else
|
||||
to_chat(owner, "<span class='warning'>There is no Ark!</span>")
|
||||
to_chat(owner, "<span class='warning '>There is no Ark!</span>")
|
||||
*/
|
||||
|
||||
//Warps to a chosen Obelisk
|
||||
|
||||
@@ -0,0 +1,432 @@
|
||||
//Clockwork guardian: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian
|
||||
name = "clockwork guardian"
|
||||
desc = "A slow, armored clockwork machine, blazing with magenta flames. It's armed with a gladius and shield, and stands ready by its master."
|
||||
icon_state = "clockwork_guardian"
|
||||
health = 300
|
||||
maxHealth = 300
|
||||
speed = 1
|
||||
obj_damage = 40
|
||||
melee_damage_lower = 20//ranged attacks are the way to go for fighting these
|
||||
melee_damage_upper = 20
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
weather_immunities = list("lava")
|
||||
movement_type = FLYING
|
||||
AIStatus = AI_OFF //this has to be manually set so that the guardian doesn't start bashing the host, how annoying -_-
|
||||
loot = list(/obj/structure/destructible/clockwork/taunting_trail)
|
||||
var/true_name = "Meme Master 69" //Required to call forth the guardian
|
||||
var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort")
|
||||
var/mob/living/host //The mob that the guardian is living inside of
|
||||
var/recovering = FALSE //If the guardian is recovering from recalling
|
||||
var/blockchance = 17 //chance to block attacks entirely
|
||||
var/counterchance = 30 //chance to counterattack after blocking
|
||||
var/static/list/damage_heal_order = list(OXY, BURN, BRUTE, TOX) //we heal our host's damage in this order
|
||||
light_color = "#AF0AAF"
|
||||
light_range = 2
|
||||
light_power = 1.1
|
||||
playstyle_string = "<span class='sevtug'>You are a clockwork guardian</span><b>, a living extension of Sevtug's will. As a guardian, you are somewhat slow, but may block attacks, \
|
||||
and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \
|
||||
Your primary goal is to serve the creature that you are now a part of, as well as The Clockwork Justiciar, Ratvar. You can use <span class='sevtug_small'><i>The Hierophant Network</i></span> to communicate silently with your master and their allies, \
|
||||
but can only exit if your master calls your true name or if they are exceptionally damaged. \
|
||||
\n\n\
|
||||
Stay near your host to protect and heal them; being too far from your host will rapidly cause you massive damage. Recall to your host if you are too weak and believe you cannot continue \
|
||||
fighting safely. As a final note, you should probably avoid harming any fellow servants of Ratvar.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/Initialize()
|
||||
. = ..()
|
||||
true_name = pick(possible_true_names)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/BiologicalLife(seconds, times_fired)
|
||||
..()
|
||||
if(is_in_host())
|
||||
if(!is_servant_of_ratvar(host))
|
||||
emerge_from_host(FALSE, TRUE)
|
||||
unbind_from_host()
|
||||
return
|
||||
if(!GLOB.ratvar_awakens && host.stat == DEAD)
|
||||
death()
|
||||
return
|
||||
if(GLOB.ratvar_awakens)
|
||||
adjustHealth(-50)
|
||||
else
|
||||
adjustHealth(-10)
|
||||
if(!recovering)
|
||||
heal_host() //also heal our host if inside of them and we aren't recovering
|
||||
else if(health == maxHealth)
|
||||
to_chat(src, "<span class='userdanger'>Your strength has returned. You can once again come forward!</span>")
|
||||
to_chat(host, "<span class='userdanger'>Your guardian is now strong enough to come forward again!</span>")
|
||||
recovering = FALSE
|
||||
else
|
||||
if(GLOB.ratvar_awakens) //If Ratvar is alive, guardians don't need a host and are downright impossible to kill
|
||||
adjustHealth(-5)
|
||||
heal_host()
|
||||
else if(host)
|
||||
if(!is_servant_of_ratvar(host))
|
||||
unbind_from_host()
|
||||
return
|
||||
if(host.stat == DEAD)
|
||||
adjustHealth(50)
|
||||
to_chat(src, "<span class='userdanger'>Your host is dead!</span>")
|
||||
return
|
||||
if(z && host.z && z == host.z)
|
||||
switch(get_dist(get_turf(src), get_turf(host)))
|
||||
if(2)
|
||||
adjustHealth(-1)
|
||||
if(3)
|
||||
//EQUILIBRIUM
|
||||
if(4)
|
||||
adjustHealth(1)
|
||||
if(5)
|
||||
adjustHealth(3)
|
||||
if(6)
|
||||
adjustHealth(6)
|
||||
if(7)
|
||||
adjustHealth(9)
|
||||
if(8 to INFINITY)
|
||||
adjustHealth(15)
|
||||
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
|
||||
else //right next to or on top of host
|
||||
adjustHealth(-2)
|
||||
heal_host() //gradually heal host if nearby and host is very weak
|
||||
else //well then, you're not even in the same zlevel
|
||||
adjustHealth(15)
|
||||
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/death(gibbed)
|
||||
emerge_from_host(FALSE, TRUE)
|
||||
unbind_from_host()
|
||||
visible_message("<span class='warning'>[src]'s equipment fades into a purple mist as the flames within sputter and dissipate.</span>", \
|
||||
"<span class='userdanger'>Your equipment fades away. You feel a moment of confusion before your fragile form is annihilated.</span>")
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Current True Name: [true_name]")
|
||||
stat(null, "Host: [host ? host : "NONE"]")
|
||||
if(host)
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
stat(null, "Host Health: [resulthealth]%")
|
||||
if(GLOB.ratvar_awakens)
|
||||
stat(null, "You are [recovering ? "un" : ""]able to deploy!")
|
||||
else
|
||||
if(resulthealth > GUARDIAN_EMERGE_THRESHOLD)
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy on hearing your True Name"]!")
|
||||
else
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy to protect your host"]!")
|
||||
stat(null, "You do [melee_damage_upper] damage on melee attacks.")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/bind_to_host(mob/living/new_host)
|
||||
if(!new_host)
|
||||
return FALSE
|
||||
host = new_host
|
||||
var/datum/action/innate/summon_guardian/SG = new()
|
||||
SG.linked_guardian = src
|
||||
SG.Grant(host)
|
||||
var/datum/action/innate/linked_minds/LM = new()
|
||||
LM.linked_guardian = src
|
||||
LM.Grant(host)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/unbind_from_host()
|
||||
if(host)
|
||||
for(var/datum/action/innate/summon_guardian/SG in host.actions)
|
||||
qdel(SG)
|
||||
for(var/datum/action/innate/linked_minds/LM in host.actions)
|
||||
qdel(LM)
|
||||
host = null
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//DAMAGE and FATIGUE
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/heal_host()
|
||||
if(!host)
|
||||
return
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
if(GLOB.ratvar_awakens || resulthealth <= GUARDIAN_EMERGE_THRESHOLD)
|
||||
new /obj/effect/temp_visual/heal(host.loc, "#AF0AAF")
|
||||
host.heal_ordered_damage(4, damage_heal_order)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(amount > 0)
|
||||
for(var/mob/living/L in view(2, src))
|
||||
if(L.is_holding_item_of_type(/obj/item/nullrod))
|
||||
to_chat(src, "<span class='userdanger'>The presence of a brandished holy artifact weakens your armor!</span>")
|
||||
amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage
|
||||
break
|
||||
. = ..()
|
||||
if(src && updating_health)
|
||||
update_health_hud()
|
||||
update_stats()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/update_health_hud()
|
||||
if(hud_used && hud_used.healths)
|
||||
if(istype(hud_used, /datum/hud))
|
||||
var/datum/hud/marauder/G = hud_used
|
||||
var/resulthealth
|
||||
if(host)
|
||||
if(iscarbon(host))
|
||||
resulthealth = "[round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)]%"
|
||||
else
|
||||
resulthealth = "[round((host.health / host.maxHealth) * 100, 0.5)]%"
|
||||
else
|
||||
resulthealth = "NONE"
|
||||
G.hosthealth.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>HOST<br>[resulthealth]</font></div>"
|
||||
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>[round((health / maxHealth) * 100, 0.5)]%</font>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/update_stats()
|
||||
if(GLOB.ratvar_awakens)
|
||||
speed = 0
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
attack_verb_continuous = "devastates"
|
||||
else
|
||||
var/healthpercent = (health/maxHealth) * 100
|
||||
switch(healthpercent)
|
||||
if(100 to 70) //Bonuses to speed and damage at high health
|
||||
speed = 0
|
||||
melee_damage_lower = 16
|
||||
melee_damage_upper = 16
|
||||
attack_verb_continuous = "viciously slashes"
|
||||
if(70 to 40)
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
attack_verb_continuous = initial(attack_verb_continuous)
|
||||
if(40 to 30) //Damage decrease, but not speed
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
attack_verb_continuous = "lightly slashes"
|
||||
if(30 to 20) //Speed decrease
|
||||
speed = 2
|
||||
melee_damage_lower = 8
|
||||
melee_damage_upper = 8
|
||||
attack_verb_continuous = "lightly slashes"
|
||||
if(20 to 10) //Massive speed decrease and weak melee attacks
|
||||
speed = 3
|
||||
melee_damage_lower = 6
|
||||
melee_damage_upper = 6
|
||||
attack_verb_continuous = "weakly slashes"
|
||||
if(10 to 0) //We are super weak and going to die
|
||||
speed = 4
|
||||
melee_damage_lower = 4
|
||||
melee_damage_upper = 4
|
||||
attack_verb_continuous = "taps"
|
||||
|
||||
//ATTACKING, BLOCKING, and COUNTERING
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/AttackingTarget()
|
||||
if(is_in_host())
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/bullet_act(obj/item/projectile/Proj)
|
||||
if(blockOrCounter(null, Proj))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/hitby(atom/movable/AM, skipcatch, hitpush, blocked, atom/movable/AM, datum/thrownthing/throwingdatum)
|
||||
if(blockOrCounter(null, AM))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attack_animal(mob/living/simple_animal/M)
|
||||
if(istype(M, /mob/living/simple_animal/hostile/clockwork/guardian) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another guardian
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attack_hand(mob/living/carbon/human/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/blockOrCounter(mob/target, atom/textobject)
|
||||
if(GLOB.ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance
|
||||
blockchance = 90
|
||||
counterchance = 90
|
||||
if(prob(blockchance))
|
||||
. = TRUE
|
||||
if(target)
|
||||
target.do_attack_animation(src)
|
||||
target.DelayNextAction(CLICK_CD_MELEE)
|
||||
blockchance = initial(blockchance)
|
||||
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 30, 1, 0, 1) //clang
|
||||
visible_message("<span class='boldannounce'>[src] blocks [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>", \
|
||||
"<span class='userdanger'>You block [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>")
|
||||
if(target && Adjacent(target))
|
||||
if(prob(counterchance))
|
||||
counterchance = initial(counterchance)
|
||||
var/previousattack_verb_continuous = attack_verb_continuous
|
||||
attack_verb_continuous = "counters"
|
||||
UnarmedAttack(target)
|
||||
attack_verb_continuous = previousattack_verb_continuous
|
||||
else
|
||||
counterchance = min(counterchance + initial(counterchance), 100)
|
||||
else
|
||||
blockchance = min(blockchance + initial(blockchance), 100)
|
||||
if(GLOB.ratvar_awakens)
|
||||
blockchance = 90
|
||||
counterchance = 90
|
||||
|
||||
//COMMUNICATION and EMERGENCE
|
||||
/*
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/handle_inherent_channels(message, message_mode)
|
||||
if(host && (is_in_host() || message_mode == MODE_BINARY))
|
||||
guardian_comms(message)
|
||||
return TRUE
|
||||
return ..()
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/guardian_comms(message)
|
||||
var/name_part = "<span class='sevtug'>[src] ([true_name])</span>"
|
||||
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
|
||||
to_chat(src, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
to_chat(host, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
for(var/M in GLOB.mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[findtextEx(host.name, host.real_name) ? "[host.name]" : "[host.real_name] (as [host.name])"]</span><span class='sevtug_small'>):</span> [message] ")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/return_to_host()
|
||||
if(is_in_host())
|
||||
return FALSE
|
||||
if(!host)
|
||||
to_chat(src, "<span class='warning'>You don't have a host!</span>")
|
||||
return FALSE
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
host.visible_message("<span class='warning'>[host]'s skin flashes magenta!</span>", "<span class='sevtug'>You feel [true_name]'s consciousness settle in your mind.</span>")
|
||||
visible_message("<span class='warning'>[src] suddenly disappears!</span>", "<span class='sevtug'>You return to [host].</span>")
|
||||
forceMove(host)
|
||||
if(resulthealth > GUARDIAN_EMERGE_THRESHOLD && health != maxHealth)
|
||||
recovering = TRUE
|
||||
to_chat(src, "<span class='userdanger'>You have weakened and will need to recover before manifesting again!</span>")
|
||||
to_chat(host, "<span class='sevtug'>[true_name] has weakened and will need to recover before manifesting again!</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/try_emerge()
|
||||
if(!host)
|
||||
to_chat(src, "<span class='warning'>You don't have a host!</span>")
|
||||
return FALSE
|
||||
if(!GLOB.ratvar_awakens)
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
if(host.stat != DEAD && resulthealth > GUARDIAN_EMERGE_THRESHOLD) //if above 20 health, fails
|
||||
to_chat(src, "<span class='warning'>Your host must be at [GUARDIAN_EMERGE_THRESHOLD]% or less health to emerge like this!</span>")
|
||||
return FALSE
|
||||
return emerge_from_host(FALSE)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - guardians can NOT exit at will, but they CAN return
|
||||
if(!is_in_host())
|
||||
return FALSE
|
||||
if(!force && recovering)
|
||||
if(hostchosen)
|
||||
to_chat(host, "<span class='sevtug'>[true_name] is too weak to come forth!</span>")
|
||||
else
|
||||
to_chat(host, "<span class='sevtug'>[true_name] tries to emerge to protect you, but it's too weak!</span>")
|
||||
to_chat(src, "<span class='userdanger'>You try to come forth, but you're too weak!</span>")
|
||||
return FALSE
|
||||
if(!force)
|
||||
if(hostchosen) //guardian approved
|
||||
to_chat(host, "<span class='sevtug'>Your words echo with power as [true_name] emerges from your body!</span>")
|
||||
else
|
||||
to_chat(host, "<span class='sevtug'>[true_name] emerges from your body to protect you!</span>")
|
||||
forceMove(host.loc)
|
||||
visible_message("<span class='warning'>[host]'s skin glows red as [name] emerges from their body!</span>", "<span class='sevtug_small'>You exit the safety of [host]'s body!</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/get_alt_name()
|
||||
return " ([text2ratvar(true_name)])"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/guardian/proc/is_in_host() //Checks if the guardian is inside of their host
|
||||
return host && loc == host
|
||||
|
||||
//HOST ACTIONS
|
||||
|
||||
//Summon guardian action: Calls forth or recalls your guardian
|
||||
/datum/action/innate/summon_guardian
|
||||
name = "Force Guardian to Emerge/Recall"
|
||||
desc = "Allows you to force your clockwork guardian to emerge or recall as required."
|
||||
button_icon_state = "clockwork_marauder"
|
||||
background_icon_state = "bg_clock"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
buttontooltipstyle = "clockcult"
|
||||
var/mob/living/simple_animal/hostile/clockwork/guardian/linked_guardian
|
||||
var/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me")
|
||||
var/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat")
|
||||
|
||||
/datum/action/innate/summon_guardian/IsAvailable()
|
||||
if(!linked_guardian)
|
||||
return FALSE
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
if(!L.can_speak_vocal() || L.stat)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/summon_guardian/Activate()
|
||||
if(linked_guardian.is_in_host())
|
||||
clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_guardian.true_name]!"))
|
||||
linked_guardian.emerge_from_host(TRUE)
|
||||
else
|
||||
clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_guardian.true_name]!"))
|
||||
linked_guardian.return_to_host()
|
||||
return TRUE
|
||||
|
||||
//Linked Minds action: talks to your guardian
|
||||
/datum/action/innate/linked_minds
|
||||
name = "Linked Minds"
|
||||
desc = "Allows you to silently communicate with your guardian."
|
||||
button_icon_state = "linked_minds"
|
||||
background_icon_state = "bg_clock"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
buttontooltipstyle = "clockcult"
|
||||
var/mob/living/simple_animal/hostile/clockwork/guardian/linked_guardian
|
||||
|
||||
/datum/action/innate/linked_minds/IsAvailable()
|
||||
if(!linked_guardian)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/linked_minds/Activate()
|
||||
var/message = stripped_input(owner, "Enter a message to tell your guardian.", "Telepathy")
|
||||
if(!owner || !message)
|
||||
return FALSE
|
||||
if(!linked_guardian)
|
||||
to_chat(owner, "<span class='warning'>Your guardian seems to have been destroyed!</span>")
|
||||
return FALSE
|
||||
var/name_part = "<span class='sevtug'>Servant [findtextEx(owner.name, owner.real_name) ? "[owner.name]" : "[owner.real_name] (as [owner.name])"]</span>"
|
||||
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
|
||||
to_chat(owner, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
to_chat(linked_guardian, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
for(var/M in GLOB.mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[linked_guardian] ([linked_guardian.true_name])</span><span class='sevtug_small'>):</span> [message]")
|
||||
return TRUE
|
||||
@@ -123,437 +123,3 @@
|
||||
|
||||
#undef MARAUDER_SLOWDOWN_PERCENTAGE
|
||||
#undef MARAUDER_SHIELD_REGEN_TIME
|
||||
|
||||
//Clockwork guardian: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian
|
||||
name = "clockwork guardian"
|
||||
desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield and stands ready by its master."
|
||||
icon_state = "clockwork_marauder"
|
||||
health = 300
|
||||
maxHealth = 300
|
||||
speed = 1
|
||||
obj_damage = 40
|
||||
melee_damage_lower = 12
|
||||
melee_damage_upper = 12
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
weather_immunities = list("lava")
|
||||
movement_type = FLYING
|
||||
AIStatus = AI_OFF //this has to be manually set so that the guardian doesn't start bashing the host, how annoying -_-
|
||||
loot = list(/obj/item/clockwork/component/geis_capacitor/fallen_armor)
|
||||
max_shield_health = 0
|
||||
shield_health = 0
|
||||
var/true_name = "Meme Master 69" //Required to call forth the guardian
|
||||
var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort")
|
||||
var/mob/living/host //The mob that the guardian is living inside of
|
||||
var/recovering = FALSE //If the guardian is recovering from recalling
|
||||
var/blockchance = 17 //chance to block attacks entirely
|
||||
var/counterchance = 30 //chance to counterattack after blocking
|
||||
var/static/list/damage_heal_order = list(OXY, BURN, BRUTE, TOX) //we heal our host's damage in this order
|
||||
light_range = 2
|
||||
light_power = 1.1
|
||||
playstyle_string = "<span class='sevtug'>You are a clockwork guardian</span><b>, a living extension of Sevtug's will. As a guardian, you are somewhat slow, but may block attacks, \
|
||||
and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \
|
||||
Your primary goal is to serve the creature that you are now a part of, as well as The Clockwork Justiciar, Ratvar. You can use <span class='sevtug_small'><i>The Hierophant Network</i></span> to communicate silently with your master and their allies, \
|
||||
but can only exit if your master calls your true name or if they are exceptionally damaged. \
|
||||
\n\n\
|
||||
Stay near your host to protect and heal them; being too far from your host will rapidly cause you massive damage. Recall to your host if you are too weak and believe you cannot continue \
|
||||
fighting safely. As a final note, you should probably avoid harming any fellow servants of Ratvar.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Initialize()
|
||||
. = ..()
|
||||
true_name = pick(possible_true_names)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/BiologicalLife(seconds, times_fired)
|
||||
..()
|
||||
if(is_in_host())
|
||||
if(!is_servant_of_ratvar(host))
|
||||
emerge_from_host(FALSE, TRUE)
|
||||
unbind_from_host()
|
||||
return
|
||||
if(!GLOB.ratvar_awakens && host.stat == DEAD)
|
||||
death()
|
||||
return
|
||||
if(GLOB.ratvar_awakens)
|
||||
adjustHealth(-50)
|
||||
else
|
||||
adjustHealth(-10)
|
||||
if(!recovering)
|
||||
heal_host() //also heal our host if inside of them and we aren't recovering
|
||||
else if(health == maxHealth)
|
||||
to_chat(src, "<span class='userdanger'>Your strength has returned. You can once again come forward!</span>")
|
||||
to_chat(host, "<span class='userdanger'>Your guardian is now strong enough to come forward again!</span>")
|
||||
recovering = FALSE
|
||||
else
|
||||
if(GLOB.ratvar_awakens) //If Ratvar is alive, guardians don't need a host and are downright impossible to kill
|
||||
adjustHealth(-5)
|
||||
heal_host()
|
||||
else if(host)
|
||||
if(!is_servant_of_ratvar(host))
|
||||
unbind_from_host()
|
||||
return
|
||||
if(host.stat == DEAD)
|
||||
adjustHealth(50)
|
||||
to_chat(src, "<span class='userdanger'>Your host is dead!</span>")
|
||||
return
|
||||
if(z && host.z && z == host.z)
|
||||
switch(get_dist(get_turf(src), get_turf(host)))
|
||||
if(2)
|
||||
adjustHealth(-1)
|
||||
if(3)
|
||||
//EQUILIBRIUM
|
||||
if(4)
|
||||
adjustHealth(1)
|
||||
if(5)
|
||||
adjustHealth(3)
|
||||
if(6)
|
||||
adjustHealth(6)
|
||||
if(7)
|
||||
adjustHealth(9)
|
||||
if(8 to INFINITY)
|
||||
adjustHealth(15)
|
||||
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
|
||||
else //right next to or on top of host
|
||||
adjustHealth(-2)
|
||||
heal_host() //gradually heal host if nearby and host is very weak
|
||||
else //well then, you're not even in the same zlevel
|
||||
adjustHealth(15)
|
||||
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/death(gibbed)
|
||||
emerge_from_host(FALSE, TRUE)
|
||||
unbind_from_host()
|
||||
visible_message("<span class='warning'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
|
||||
"<span class='userdanger'>Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.</span>")
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Current True Name: [true_name]")
|
||||
stat(null, "Host: [host ? host : "NONE"]")
|
||||
if(host)
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
stat(null, "Host Health: [resulthealth]%")
|
||||
if(GLOB.ratvar_awakens)
|
||||
stat(null, "You are [recovering ? "un" : ""]able to deploy!")
|
||||
else
|
||||
if(resulthealth > GUARDIAN_EMERGE_THRESHOLD)
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy on hearing your True Name"]!")
|
||||
else
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy to protect your host"]!")
|
||||
stat(null, "You do [melee_damage_upper] damage on melee attacks.")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/bind_to_host(mob/living/new_host)
|
||||
if(!new_host)
|
||||
return FALSE
|
||||
host = new_host
|
||||
var/datum/action/innate/summon_guardian/SG = new()
|
||||
SG.linked_guardian = src
|
||||
SG.Grant(host)
|
||||
var/datum/action/innate/linked_minds/LM = new()
|
||||
LM.linked_guardian = src
|
||||
LM.Grant(host)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/unbind_from_host()
|
||||
if(host)
|
||||
for(var/datum/action/innate/summon_guardian/SG in host.actions)
|
||||
qdel(SG)
|
||||
for(var/datum/action/innate/linked_minds/LM in host.actions)
|
||||
qdel(LM)
|
||||
host = null
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//DAMAGE and FATIGUE
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/heal_host()
|
||||
if(!host)
|
||||
return
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
if(GLOB.ratvar_awakens || resulthealth <= GUARDIAN_EMERGE_THRESHOLD)
|
||||
new /obj/effect/temp_visual/heal(host.loc, "#AF0AAF")
|
||||
host.heal_ordered_damage(4, damage_heal_order)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(amount > 0)
|
||||
for(var/mob/living/L in view(2, src))
|
||||
if(L.is_holding_item_of_type(/obj/item/nullrod))
|
||||
to_chat(src, "<span class='userdanger'>The presence of a brandished holy artifact weakens your armor!</span>")
|
||||
amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage
|
||||
break
|
||||
. = ..()
|
||||
if(src && updating_health)
|
||||
update_health_hud()
|
||||
update_stats()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/update_health_hud()
|
||||
if(hud_used && hud_used.healths)
|
||||
if(istype(hud_used, /datum/hud/marauder))
|
||||
var/datum/hud/marauder/G = hud_used
|
||||
var/resulthealth
|
||||
if(host)
|
||||
if(iscarbon(host))
|
||||
resulthealth = "[round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)]%"
|
||||
else
|
||||
resulthealth = "[round((host.health / host.maxHealth) * 100, 0.5)]%"
|
||||
else
|
||||
resulthealth = "NONE"
|
||||
G.hosthealth.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>HOST<br>[resulthealth]</font></div>"
|
||||
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>[round((health / maxHealth) * 100, 0.5)]%</font>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/update_stats()
|
||||
if(GLOB.ratvar_awakens)
|
||||
speed = 0
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
attack_verb_continuous = "devastates"
|
||||
else
|
||||
var/healthpercent = (health/maxHealth) * 100
|
||||
switch(healthpercent)
|
||||
if(100 to 70) //Bonuses to speed and damage at high health
|
||||
speed = 0
|
||||
melee_damage_lower = 16
|
||||
melee_damage_upper = 16
|
||||
attack_verb_continuous = "viciously slashes"
|
||||
if(70 to 40)
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
attack_verb_continuous = initial(attack_verb_continuous)
|
||||
if(40 to 30) //Damage decrease, but not speed
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
attack_verb_continuous = "lightly slashes"
|
||||
if(30 to 20) //Speed decrease
|
||||
speed = 2
|
||||
melee_damage_lower = 8
|
||||
melee_damage_upper = 8
|
||||
attack_verb_continuous = "lightly slashes"
|
||||
if(20 to 10) //Massive speed decrease and weak melee attacks
|
||||
speed = 3
|
||||
melee_damage_lower = 6
|
||||
melee_damage_upper = 6
|
||||
attack_verb_continuous = "weakly slashes"
|
||||
if(10 to 0) //We are super weak and going to die
|
||||
speed = 4
|
||||
melee_damage_lower = 4
|
||||
melee_damage_upper = 4
|
||||
attack_verb_continuous = "taps"
|
||||
|
||||
//ATTACKING, BLOCKING, and COUNTERING
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/AttackingTarget()
|
||||
if(is_in_host())
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/bullet_act(obj/item/projectile/Proj)
|
||||
if(blockOrCounter(null, Proj))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/hitby(atom/movable/AM, skipcatch, hitpush, blocked, atom/movable/AM, datum/thrownthing/throwingdatum)
|
||||
if(blockOrCounter(null, AM))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_animal(mob/living/simple_animal/M)
|
||||
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder/guardian) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another guardian
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_hand(mob/living/carbon/human/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/blockOrCounter(mob/target, atom/textobject)
|
||||
if(GLOB.ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance
|
||||
blockchance = 90
|
||||
counterchance = 90
|
||||
if(prob(blockchance))
|
||||
. = TRUE
|
||||
if(target)
|
||||
target.do_attack_animation(src)
|
||||
target.DelayNextAction(CLICK_CD_MELEE)
|
||||
blockchance = initial(blockchance)
|
||||
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 30, 1, 0, 1) //clang
|
||||
visible_message("<span class='boldannounce'>[src] blocks [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>", \
|
||||
"<span class='userdanger'>You block [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>")
|
||||
if(target && Adjacent(target))
|
||||
if(prob(counterchance))
|
||||
counterchance = initial(counterchance)
|
||||
var/previousattack_verb_continuous = attack_verb_continuous
|
||||
attack_verb_continuous = "counters"
|
||||
UnarmedAttack(target)
|
||||
attack_verb_continuous = previousattack_verb_continuous
|
||||
else
|
||||
counterchance = min(counterchance + initial(counterchance), 100)
|
||||
else
|
||||
blockchance = min(blockchance + initial(blockchance), 100)
|
||||
if(GLOB.ratvar_awakens)
|
||||
blockchance = 90
|
||||
counterchance = 90
|
||||
|
||||
//COMMUNICATION and EMERGENCE
|
||||
/*
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/handle_inherent_channels(message, message_mode)
|
||||
if(host && (is_in_host() || message_mode == MODE_BINARY))
|
||||
guardian_comms(message)
|
||||
return TRUE
|
||||
return ..()
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/guardian_comms(message)
|
||||
var/name_part = "<span class='sevtug'>[src] ([true_name])</span>"
|
||||
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
|
||||
to_chat(src, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
to_chat(host, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
for(var/M in GLOB.mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[findtextEx(host.name, host.real_name) ? "[host.name]" : "[host.real_name] (as [host.name])"]</span><span class='sevtug_small'>):</span> [message] ")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/return_to_host()
|
||||
if(is_in_host())
|
||||
return FALSE
|
||||
if(!host)
|
||||
to_chat(src, "<span class='warning'>You don't have a host!</span>")
|
||||
return FALSE
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='sevtug'>You feel [true_name]'s consciousness settle in your mind.</span>")
|
||||
visible_message("<span class='warning'>[src] suddenly disappears!</span>", "<span class='sevtug'>You return to [host].</span>")
|
||||
forceMove(host)
|
||||
if(resulthealth > GUARDIAN_EMERGE_THRESHOLD && health != maxHealth)
|
||||
recovering = TRUE
|
||||
to_chat(src, "<span class='userdanger'>You have weakened and will need to recover before manifesting again!</span>")
|
||||
to_chat(host, "<span class='sevtug'>[true_name] has weakened and will need to recover before manifesting again!</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/try_emerge()
|
||||
if(!host)
|
||||
to_chat(src, "<span class='warning'>You don't have a host!</span>")
|
||||
return FALSE
|
||||
if(!GLOB.ratvar_awakens)
|
||||
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
|
||||
if(iscarbon(host))
|
||||
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
|
||||
if(host.stat != DEAD && resulthealth > GUARDIAN_EMERGE_THRESHOLD) //if above 20 health, fails
|
||||
to_chat(src, "<span class='warning'>Your host must be at [GUARDIAN_EMERGE_THRESHOLD]% or less health to emerge like this!</span>")
|
||||
return FALSE
|
||||
return emerge_from_host(FALSE)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - guardians can NOT exit at will, but they CAN return
|
||||
if(!is_in_host())
|
||||
return FALSE
|
||||
if(!force && recovering)
|
||||
if(hostchosen)
|
||||
to_chat(host, "<span class='sevtug'>[true_name] is too weak to come forth!</span>")
|
||||
else
|
||||
to_chat(host, "<span class='sevtug'>[true_name] tries to emerge to protect you, but it's too weak!</span>")
|
||||
to_chat(src, "<span class='userdanger'>You try to come forth, but you're too weak!</span>")
|
||||
return FALSE
|
||||
if(!force)
|
||||
if(hostchosen) //guardian approved
|
||||
to_chat(host, "<span class='sevtug'>Your words echo with power as [true_name] emerges from your body!</span>")
|
||||
else
|
||||
to_chat(host, "<span class='sevtug'>[true_name] emerges from your body to protect you!</span>")
|
||||
forceMove(host.loc)
|
||||
visible_message("<span class='warning'>[host]'s skin glows red as [name] emerges from their body!</span>", "<span class='sevtug_small'>You exit the safety of [host]'s body!</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/get_alt_name()
|
||||
return " ([text2ratvar(true_name)])"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/is_in_host() //Checks if the guardian is inside of their host
|
||||
return host && loc == host
|
||||
|
||||
//HOST ACTIONS
|
||||
|
||||
//Summon guardian action: Calls forth or recalls your guardian
|
||||
/datum/action/innate/summon_guardian
|
||||
name = "Force Guardian to Emerge/Recall"
|
||||
desc = "Allows you to force your clockwork guardian to emerge or recall as required."
|
||||
button_icon_state = "clockwork_marauder"
|
||||
background_icon_state = "bg_clock"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
buttontooltipstyle = "clockcult"
|
||||
var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/linked_guardian
|
||||
var/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me")
|
||||
var/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat")
|
||||
|
||||
/datum/action/innate/summon_guardian/IsAvailable()
|
||||
if(!linked_guardian)
|
||||
return FALSE
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
if(!L.can_speak_vocal() || L.stat)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/summon_guardian/Activate()
|
||||
if(linked_guardian.is_in_host())
|
||||
clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_guardian.true_name]!"))
|
||||
linked_guardian.emerge_from_host(TRUE)
|
||||
else
|
||||
clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_guardian.true_name]!"))
|
||||
linked_guardian.return_to_host()
|
||||
return TRUE
|
||||
|
||||
//Linked Minds action: talks to your guardian
|
||||
/datum/action/innate/linked_minds
|
||||
name = "Linked Minds"
|
||||
desc = "Allows you to silently communicate with your guardian."
|
||||
button_icon_state = "linked_minds"
|
||||
background_icon_state = "bg_clock"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
buttontooltipstyle = "clockcult"
|
||||
var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/linked_guardian
|
||||
|
||||
/datum/action/innate/linked_minds/IsAvailable()
|
||||
if(!linked_guardian)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/linked_minds/Activate()
|
||||
var/message = stripped_input(owner, "Enter a message to tell your guardian.", "Telepathy")
|
||||
if(!owner || !message)
|
||||
return FALSE
|
||||
if(!linked_guardian)
|
||||
to_chat(owner, "<span class='warning'>Your guardian seems to have been destroyed!</span>")
|
||||
return FALSE
|
||||
var/name_part = "<span class='sevtug'>Servant [findtextEx(owner.name, owner.real_name) ? "[owner.name]" : "[owner.real_name] (as [owner.name])"]</span>"
|
||||
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
|
||||
to_chat(owner, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
to_chat(linked_guardian, "[name_part]<span class='sevtug_small'>:</span> [message]")
|
||||
for(var/M in GLOB.mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[linked_guardian] ([linked_guardian.true_name])</span><span class='sevtug_small'>):</span> [message]")
|
||||
return TRUE
|
||||
|
||||
@@ -5,7 +5,7 @@ Pieces of scripture require certain follower counts, contruction value, and acti
|
||||
Drivers: Unlocked by default
|
||||
Scripts: 35k power or one convert
|
||||
Applications: 50k or three converts
|
||||
Judgement 5 converts
|
||||
Judgement 80k power or nine converts
|
||||
*/
|
||||
|
||||
/datum/clockwork_scripture
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
descname = "Powers Nearby Structures"
|
||||
name = "Sigil of Transmission"
|
||||
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
|
||||
invocations = list("Divinity...", "...power our creations!")
|
||||
invocations = list("Divinity...", "...power our creations.")
|
||||
channel_time = 70
|
||||
power_cost = 200
|
||||
whispered = TRUE
|
||||
@@ -18,7 +18,7 @@
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
one_per_tile = TRUE
|
||||
primary_component = HIEROPHANT_ANSIBLE
|
||||
sort_priority = 1
|
||||
sort_priority = 2
|
||||
important = TRUE
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
|
||||
@@ -28,7 +28,7 @@
|
||||
descname = "Powered Structure, Delay Emergency Shuttles"
|
||||
name = "Prolonging Prism"
|
||||
desc = "Creates a mechanized prism which will delay the arrival of an emergency shuttle by 2 minutes at a massive power cost."
|
||||
invocations = list("May this prism...", "...grant us time to enact his will!")
|
||||
invocations = list("May this prism...", "...grant us time to enact his will.")
|
||||
channel_time = 80
|
||||
power_cost = 300
|
||||
object_path = /obj/structure/destructible/clockwork/powered/prolonging_prism
|
||||
@@ -60,7 +60,7 @@
|
||||
descname = "Powered Structure, Area Denial"
|
||||
name = "Mania Motor"
|
||||
desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
|
||||
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
|
||||
invocations = list("May this transmitter...", "...break the will of all who oppose us.")
|
||||
channel_time = 80
|
||||
power_cost = 750
|
||||
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
|
||||
@@ -72,7 +72,7 @@
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
one_per_tile = TRUE
|
||||
primary_component = HIEROPHANT_ANSIBLE
|
||||
sort_priority = 2
|
||||
sort_priority = 5
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
|
||||
requires_full_power = TRUE
|
||||
@@ -83,7 +83,7 @@
|
||||
descname = "Powered Structure, Teleportation Hub"
|
||||
name = "Clockwork Obelisk"
|
||||
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
|
||||
invocations = list("May this obelisk...", "...take us to all places!")
|
||||
invocations = list("May this obelisk...", "...take us to all places.")
|
||||
channel_time = 80
|
||||
power_cost = 300
|
||||
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
//Memory Allocation: Finds a willing ghost and makes them into a clockwork guardian for the invoker.
|
||||
/datum/clockwork_scripture/memory_allocation
|
||||
descname = "Personal Guardian, A Peice Of Your Mind."
|
||||
descname = "Personal Guardian housed in the brain."
|
||||
name = "Memory Allocation"
|
||||
desc = "Allocates part of your consciousness to a Clockwork Guardian, a variant of Marauder that lives within you, able to be \
|
||||
called forth by Speaking its True Name or if you become exceptionally low on health.<br>\
|
||||
@@ -109,13 +109,13 @@
|
||||
invocations = list("Fright's will...", "...call forth...")
|
||||
channel_time = 100
|
||||
power_cost = 8000
|
||||
usage_tip = "guardians are useful as personal bodyguards and frontline warriors."
|
||||
usage_tip = "Guardians are useful as personal bodyguards and frontline warriors."
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
primary_component = GEIS_CAPACITOR
|
||||
sort_priority = 5
|
||||
sort_priority = 6
|
||||
|
||||
/datum/clockwork_scripture/memory_allocation/check_special_requirements()
|
||||
for(var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/M in GLOB.all_clockwork_mobs)
|
||||
for(var/mob/living/simple_animal/hostile/clockwork/guardian/M in GLOB.all_clockwork_mobs)
|
||||
if(M.host == invoker)
|
||||
to_chat(invoker, "<span class='warning'>You can only house one guardian at a time!</span>")
|
||||
return FALSE
|
||||
@@ -151,7 +151,7 @@
|
||||
return FALSE
|
||||
clockwork_say(invoker, text2ratvar("...sword and shield!"))
|
||||
var/mob/dead/observer/theghost = pick(marauder_candidates)
|
||||
var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/M = new(invoker)
|
||||
var/mob/living/simple_animal/hostile/clockwork/guardian/M = new(invoker)
|
||||
M.key = theghost.key
|
||||
M.bind_to_host(invoker)
|
||||
invoker.visible_message("<span class='warning'>The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!</span>", \
|
||||
@@ -163,7 +163,7 @@
|
||||
descname = "Well-Rounded Combat Construct"
|
||||
name = "Clockwork Marauder"
|
||||
desc = "Creates a shell for a clockwork marauder, a balanced frontline construct that can deflect projectiles with its shield."
|
||||
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal.")
|
||||
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal!")
|
||||
channel_time = 80
|
||||
power_cost = 8000
|
||||
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a suit of thrumming armor.</span>"
|
||||
@@ -171,7 +171,7 @@
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
one_per_tile = TRUE
|
||||
primary_component = BELLIGERENT_EYE
|
||||
sort_priority = 6
|
||||
sort_priority = 7
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a clockwork marauder, used for frontline combat."
|
||||
object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder
|
||||
@@ -223,7 +223,7 @@
|
||||
object_path = /obj/mecha/combat/neovgre
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
primary_component = BELLIGERENT_EYE
|
||||
sort_priority = 7
|
||||
sort_priority = 8
|
||||
creator_message = "<span class='brass'>Neovgre, the Anima Bulwark towers over you... your enemies reckoning has come.</span>"
|
||||
|
||||
/datum/clockwork_scripture/create_object/summon_arbiter/check_special_requirements()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
descname = "Generates Power From Starlight"
|
||||
name = "Stargazer"
|
||||
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
|
||||
invocations = list("Capture their inferior light for us!")
|
||||
invocations = list("Capture their inferior light for us.")
|
||||
channel_time = 50
|
||||
power_cost = 200
|
||||
object_path = /obj/structure/destructible/clockwork/stargazer
|
||||
@@ -16,6 +16,7 @@
|
||||
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
|
||||
tier = SCRIPTURE_DRIVER
|
||||
one_per_tile = TRUE
|
||||
whispered = TRUE
|
||||
primary_component = HIEROPHANT_ANSIBLE
|
||||
sort_priority = 1
|
||||
quickbind = TRUE
|
||||
@@ -23,7 +24,8 @@
|
||||
|
||||
/datum/clockwork_scripture/create_object/stargazer/check_special_requirements()
|
||||
var/area/A = get_area(invoker)
|
||||
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
|
||||
var/turf/T = get_turf(invoker)
|
||||
if(!is_station_level(invoker.z) || isspaceturf(T) || !(A.area_flags & VALID_TERRITORY))
|
||||
to_chat(invoker, "<span class='danger'>Stargazers can't be built off-station.</span>")
|
||||
return
|
||||
return ..()
|
||||
@@ -34,7 +36,7 @@
|
||||
descname = "Power Generation"
|
||||
name = "Integration Cog"
|
||||
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
|
||||
invocations = list("Take that which sustains them!")
|
||||
invocations = list("Take that which sustains them.")
|
||||
channel_time = 10
|
||||
power_cost = 10
|
||||
whispered = TRUE
|
||||
@@ -55,7 +57,7 @@
|
||||
descname = "Trap, Stunning"
|
||||
name = "Sigil of Transgression"
|
||||
desc = "Wards a tile with a sigil, which will briefly stun the next non-Servant to cross it and apply Belligerent to them."
|
||||
invocations = list("Divinity, smite...", "...those who trespass here!")
|
||||
invocations = list("Divinity, smite...", "...those who trespass here.")
|
||||
channel_time = 50
|
||||
power_cost = 50
|
||||
whispered = TRUE
|
||||
@@ -75,7 +77,7 @@
|
||||
descname = "Trap, Conversion"
|
||||
name = "Sigil of Submission"
|
||||
desc = "Places a luminous sigil that will convert any non-Servants that remain on it for 8 seconds."
|
||||
invocations = list("Divinity, enlighten...", "...those who trespass here!")
|
||||
invocations = list("Divinity, enlighten...", "...those who trespass here.")
|
||||
channel_time = 60
|
||||
power_cost = 125
|
||||
whispered = TRUE
|
||||
@@ -95,7 +97,7 @@
|
||||
descname = "Short-Range Single-Target Stun"
|
||||
name = "Kindle"
|
||||
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
|
||||
invocations = list("Divinity, show them your light!")
|
||||
invocations = list("Divinity, show them your light.")
|
||||
whispered = TRUE
|
||||
channel_time = 25 //2.5 seconds should be a okay compromise between being able to use it when needed, and not being able to just pause in combat for a second and hardstunning your enemy
|
||||
power_cost = 125
|
||||
@@ -118,7 +120,7 @@
|
||||
descname = "Handcuffs"
|
||||
name = "Hateful Manacles"
|
||||
desc = "Forms replicant manacles around a target's wrists that function like handcuffs."
|
||||
invocations = list("Shackle the heretic!", "Break them in body and spirit!")
|
||||
invocations = list("Shackle the heretic!", "Break them in body and spirit.")
|
||||
channel_time = 15
|
||||
power_cost = 25
|
||||
whispered = TRUE
|
||||
@@ -269,7 +271,7 @@
|
||||
descname = "New Clockwork Slab"
|
||||
name = "Replicant"
|
||||
desc = "Creates a new clockwork slab."
|
||||
invocations = list("Metal, become greater!")
|
||||
invocations = list("Metal, become greater.")
|
||||
channel_time = 10
|
||||
power_cost = 25
|
||||
whispered = TRUE
|
||||
@@ -290,7 +292,7 @@
|
||||
descname = "Limited Xray Vision Glasses"
|
||||
name = "Wraith Spectacles"
|
||||
desc = "Fabricates a pair of glasses which grant true sight but cause gradual vision loss."
|
||||
invocations = list("Show the truth of this world to me!")
|
||||
invocations = list("Show the truth of this world to me.")
|
||||
channel_time = 10
|
||||
power_cost = 50
|
||||
whispered = TRUE
|
||||
@@ -310,7 +312,7 @@
|
||||
name = "Spatial Gateway"
|
||||
desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \
|
||||
Each servant assisting in the invocation adds one additional use and four additional seconds to the gateway's uses and duration."
|
||||
invocations = list("Spatial Gateway...", "...activate!")
|
||||
invocations = list("Spatial Gateway...", "...activate.")
|
||||
channel_time = 30
|
||||
power_cost = 400
|
||||
whispered = TRUE
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. It will periodically give indication of its general position to everyone on the station \
|
||||
as well as being loud enough to be heard throughout the entire sector. Defend it with your life!"
|
||||
tier = SCRIPTURE_APPLICATION
|
||||
sort_priority = 8
|
||||
sort_priority = 1
|
||||
requires_full_power = TRUE
|
||||
|
||||
/datum/clockwork_scripture/create_object/ark_of_the_clockwork_justiciar/check_special_requirements()
|
||||
@@ -34,7 +34,7 @@
|
||||
return FALSE
|
||||
var/area/A = get_area(invoker)
|
||||
var/turf/T = get_turf(invoker)
|
||||
if(!T || !is_station_level(T.z) || istype(A, /area/shuttle) || !A.blob_allowed)
|
||||
if(!is_station_level(T.z) || isspaceturf(T) || !(A.area_flags & VALID_TERRITORY) || isshuttleturf(T))
|
||||
to_chat(invoker, "<span class='warning'>You must be on the station to activate the Ark!</span>")
|
||||
return FALSE
|
||||
if(GLOB.clockwork_gateway_activated)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
descname = "Structure, Turret"
|
||||
name = "Ocular Warden"
|
||||
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
|
||||
invocations = list("Guardians of Engine...", "...judge those who would harm us!")
|
||||
invocations = list("Guardians of Engine...", "...judge those who would harm us.")
|
||||
channel_time = 100
|
||||
power_cost = 250
|
||||
object_path = /obj/structure/destructible/clockwork/ocular_warden
|
||||
@@ -105,7 +105,7 @@
|
||||
descname = "Delayed Area Knockdown Glasses"
|
||||
name = "Judicial Visor"
|
||||
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
|
||||
invocations = list("Grant me the flames of Engine!")
|
||||
invocations = list("Grant me the flames of Engine.")
|
||||
channel_time = 10
|
||||
power_cost = 400
|
||||
whispered = TRUE
|
||||
@@ -124,7 +124,7 @@
|
||||
descname = "Shield with empowerable bashes"
|
||||
name = "Nezbere's shield"
|
||||
desc = "Creates a shield which generates charge from blocking damage, using it to empower its bashes tremendously. It is repaired with brass, and while very durable, extremely weak to lasers and, even more so, to energy weaponry."
|
||||
invocations = list("Shield me...", "... from the coming dark!")
|
||||
invocations = list("Shield me...", "... from the coming dark.")
|
||||
channel_time = 20
|
||||
power_cost = 600 //Shouldn't be too spammable but not too hard to get either
|
||||
whispered = TRUE
|
||||
@@ -143,7 +143,7 @@
|
||||
descname = "Summonable Armor and Weapons"
|
||||
name = "Clockwork Armaments"
|
||||
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
|
||||
invocations = list("Grant me armaments...", "...from the forge of Armorer!")
|
||||
invocations = list("Grant me armaments...", "...from the forge of Armorer.")
|
||||
channel_time = 20
|
||||
power_cost = 250
|
||||
whispered = TRUE
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/structure/destructible/clockwork/attackby(obj/item/I, mob/user, params)
|
||||
if(is_servant_of_ratvar(user) && istype(I, /obj/item/wrench) && unanchored_icon)
|
||||
if(is_servant_of_ratvar(user) && I.tool_behaviour == TOOL_WRENCH && unanchored_icon)
|
||||
if(default_unfasten_wrench(user, I, 50) == SUCCESSFUL_UNFASTEN)
|
||||
update_anchored(user)
|
||||
return 1
|
||||
|
||||
+2
-2
@@ -81,13 +81,13 @@
|
||||
priority_announce("Massive [Gibberish("bluespace", 100)] anomaly detected on all frequencies. All crew are directed to \
|
||||
@!$, [text2ratvar("PURGE ALL UNTRUTHS")] <&. the anomalies and destroy their source to prevent further damage to corporate property. This is \
|
||||
not a drill.", "Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ark_activation_sequence.ogg')
|
||||
set_security_level("delta")
|
||||
set_security_level("Delta")
|
||||
for(var/V in SSticker.mode.servants_of_ratvar)
|
||||
var/datum/mind/M = V
|
||||
if(!M || !M.current)
|
||||
continue
|
||||
if(ishuman(M.current))
|
||||
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
|
||||
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER))
|
||||
var/turf/T = get_turf(src)
|
||||
var/list/open_turfs = list()
|
||||
for(var/turf/open/OT in orange(1, T))
|
||||
|
||||
+33
-1
@@ -29,8 +29,9 @@
|
||||
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)
|
||||
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
|
||||
SSpersistence.station_was_destroyed = TRUE
|
||||
INVOKE_ASYNC(src, .proc/purge_the_heresy)
|
||||
|
||||
|
||||
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
|
||||
GLOB.ratvar_awakens--
|
||||
@@ -151,3 +152,34 @@
|
||||
sound_to_playing_players('sound/machines/clockcult/ratvar_scream.ogg', 80)
|
||||
narsie.clashing = FALSE
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/structure/destructible/clockwork/massive/ratvar/proc/purge_the_heresy()
|
||||
sleep(50)
|
||||
priority_announce("Massive energy surge detected. Closest matching threat: Incoming supernova. All crew are advised to evacuate NAN lightyears away from blast zone","Central Command Higher Dimensional Affairs", 'sound/misc/airraid.ogg')
|
||||
sleep(300)
|
||||
priority_announce("Gravitational anomalies detected on the station. [Gibberish("There is no additional dat", 100)]-BZZZZZT.","Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ratvar_announce1.ogg')
|
||||
sleep(80)
|
||||
sound_to_playing_players('sound/magic/clockwork/ratvar_announce2.ogg', 70)
|
||||
send_to_playing_players("<span class='heavy_brass'><font size=5>\"COME, ALL THOSE FAITHFUL! WITNESS THE RAYS OF JUSTICE CAST UPON THE HERETICS!\"</font></span>")
|
||||
sleep(50)
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
SSshuttle.lockdown = TRUE
|
||||
sleep(250)
|
||||
if(QDELETED(src))
|
||||
priority_announce("Energy signal no longer detected.","Central Command Higher Dimensional Affairs")
|
||||
return
|
||||
sound_to_playing_players('sound/magic/clockwork/ark_activation_sequence.ogg', 80) //if this isn't lessened in volume it peaks for some reason
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/clockcult_ending_helper), 300)
|
||||
|
||||
/proc/clockcult_ending_helper()
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
if(M.client)
|
||||
SEND_SOUND(M, sound('sound/magic/clockwork/ratvar_attack.ogg'))
|
||||
SEND_SOUND(M, sound('sound/magic/clockwork/ratvarfire.ogg'))
|
||||
if(!is_servant_of_ratvar(M) && isliving(M))
|
||||
var/mob/living/L = M
|
||||
L.fire_stacks = INFINITY
|
||||
L.IgniteMob()
|
||||
sleep(50)
|
||||
SSticker.force_ending = 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/obj/structure/destructible/clockwork/reflector
|
||||
name = "reflector"
|
||||
desc = "A large lantern-shaped machine made of thin brass. It looks fragile."
|
||||
clockwork_desc = "A lantern-shaped generator that produces power when near starlight."
|
||||
desc = "A large mirror-like structure made of thin brass on one side. It looks fragile."
|
||||
clockwork_desc = "A large mirror-like structure made of thin brass on one side. It can redirect laser fire on one side"
|
||||
icon_state = "reflector"
|
||||
unanchored_icon = "reflector_unwrenched"
|
||||
max_integrity = 40
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
break
|
||||
if(has_starlight && anchored)
|
||||
var/area/A = get_area(src)
|
||||
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
|
||||
if(A.outdoors || A.map_name == "Space" || !(A.area_flags & VALID_TERRITORY))
|
||||
has_starlight = FALSE
|
||||
if(old_status != has_starlight)
|
||||
if(has_starlight)
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
return
|
||||
|
||||
/obj/structure/destructible/clockwork/wall_gear/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/wrench))
|
||||
if(I.tool_behaviour == TOOL_WRENCH)
|
||||
default_unfasten_wrench(user, I, 10)
|
||||
return 1
|
||||
else if(istype(I, /obj/item/screwdriver))
|
||||
else if(I.tool_behaviour == TOOL_SCREWDRIVER)
|
||||
if(anchored)
|
||||
to_chat(user, "<span class='warning'>[src] needs to be unsecured to disassemble it!</span>")
|
||||
else
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
antagpanel_category = "Clockcult"
|
||||
job_rank = ROLE_SERVANT_OF_RATVAR
|
||||
antag_moodlet = /datum/mood_event/cult
|
||||
skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
|
||||
skill_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/level/dwarfy/blacksmithing)
|
||||
var/datum/action/innate/hierophant/hierophant_network = new
|
||||
threat = 3
|
||||
var/datum/team/clockcult/clock_team
|
||||
@@ -15,13 +15,16 @@
|
||||
var/ignore_holy_water = FALSE
|
||||
|
||||
/datum/antagonist/clockcult/silent
|
||||
name = "Silent Clock Cultist"
|
||||
silent = TRUE
|
||||
show_in_antagpanel = FALSE //internal
|
||||
|
||||
/datum/antagonist/clockcult/neutered
|
||||
name = "Neutered Clock Cultist"
|
||||
neutered = TRUE
|
||||
|
||||
/datum/antagonist/clockcult/neutered/traitor
|
||||
name = "Traitor Clock Cultist"
|
||||
ignore_eligibility_check = TRUE
|
||||
ignore_holy_water = TRUE
|
||||
show_in_roundend = FALSE
|
||||
@@ -136,7 +139,7 @@
|
||||
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
|
||||
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
|
||||
if(G && G.active && ishuman(current))
|
||||
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
|
||||
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER))
|
||||
|
||||
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/current = owner.current
|
||||
@@ -185,7 +188,7 @@
|
||||
|
||||
|
||||
/datum/antagonist/clockcult/admin_add(datum/mind/new_owner,mob/admin)
|
||||
add_servant_of_ratvar(new_owner.current, TRUE)
|
||||
add_servant_of_ratvar(new_owner.current, TRUE, override_type = type)
|
||||
message_admins("[key_name_admin(admin)] has made [new_owner.current] into a servant of Ratvar.")
|
||||
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
var/ignore_holy_water = FALSE
|
||||
|
||||
/datum/antagonist/cult/neutered
|
||||
name = "Neutered Cultist"
|
||||
neutered = TRUE
|
||||
|
||||
/datum/antagonist/cult/neutered/traitor
|
||||
name = "Traitor Cultist"
|
||||
ignore_eligibility_checks = TRUE
|
||||
ignore_holy_water = TRUE
|
||||
show_in_roundend = FALSE
|
||||
@@ -330,7 +332,7 @@
|
||||
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))
|
||||
H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -ANTAG_LAYER))
|
||||
|
||||
/datum/team/cult/proc/setup_objectives()
|
||||
//SAC OBJECTIVE , todo: move this to objective internals
|
||||
@@ -417,7 +419,7 @@
|
||||
var/sanity = 0
|
||||
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
|
||||
var/area/summon = pick(GLOB.sortedAreas - summon_spots)
|
||||
if(summon && is_station_level(summon.z) && summon.valid_territory)
|
||||
if(summon && is_station_level(summon.z) && !(summon.area_flags & VALID_TERRITORY))
|
||||
summon_spots += summon
|
||||
sanity++
|
||||
update_explanation_text()
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
if(choice == "Yes" && IsAvailable())
|
||||
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if(!C.cult_team)
|
||||
to_chat(owner, "<span class='cult bold'>Do you not alreaady lead yourself?</span>")
|
||||
to_chat(owner, "<span class='cult bold'>Do you not already lead yourself?</span>")
|
||||
return
|
||||
pollCultists(owner,C.cult_team)
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ This file contains the cult dagger and rune list code
|
||||
to_chat(user, "<span class='cult'>There is already a rune here.</span>")
|
||||
return FALSE
|
||||
var/area/A = get_area(T)
|
||||
if((!is_station_level(T.z) && !is_mining_level(T.z)) || (A && !A.blob_allowed))
|
||||
if((!is_station_level(T.z) && !is_mining_level(T.z)) || !(A?.area_flags & VALID_TERRITORY))
|
||||
to_chat(user, "<span class='warning'>The veil is not weak enough here.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/mob/living/carbon/true_devil/doUnEquip(obj/item/I, force)
|
||||
/mob/living/carbon/true_devil/doUnEquip(obj/item/I, force, silent = FALSE)
|
||||
if(..())
|
||||
update_inv_hands()
|
||||
return 1
|
||||
|
||||
@@ -191,8 +191,6 @@ new /datum/disease_ability/symptom/powerful/youth
|
||||
/datum/disease_ability/action/sneeze
|
||||
name = "Voluntary Sneezing"
|
||||
actions = list(/datum/action/cooldown/disease_sneeze)
|
||||
cost = 2
|
||||
required_total_points = 3
|
||||
short_desc = "Force the host you are following to sneeze, spreading your infection to those in front of them."
|
||||
long_desc = "Force the host you are following to sneeze with extra force, spreading your infection to any victims in a 4 meter cone in front of your host.<br>Cooldown: 20 seconds"
|
||||
|
||||
@@ -229,8 +227,6 @@ new /datum/disease_ability/symptom/powerful/youth
|
||||
/datum/disease_ability/action/infect
|
||||
name = "Secrete Infection"
|
||||
actions = list(/datum/action/cooldown/disease_infect)
|
||||
cost = 2
|
||||
required_total_points = 3
|
||||
short_desc = "Cause all objects your host is touching to become infectious for a limited time, spreading your infection to anyone who touches them."
|
||||
long_desc = "Cause the host you are following to excrete an infective substance from their pores, causing all objects touching their skin to transmit your infection to anyone who touches them for the next 30 seconds. This includes the floor, if they are not wearing shoes, and any items they are holding, if they are not wearing gloves.<br>Cooldown: 40 seconds"
|
||||
|
||||
@@ -271,23 +267,20 @@ new /datum/disease_ability/symptom/powerful/youth
|
||||
//healing costs more so you have to techswitch from naughty disease otherwise we'd have friendly disease for easy greentext (no fun!)
|
||||
|
||||
/datum/disease_ability/symptom/mild
|
||||
cost = 2
|
||||
required_total_points = 4
|
||||
category = "Symptom (Weak)"
|
||||
|
||||
/datum/disease_ability/symptom/medium
|
||||
cost = 4
|
||||
required_total_points = 8
|
||||
category = "Symptom"
|
||||
|
||||
/datum/disease_ability/symptom/medium/heal
|
||||
cost = 5
|
||||
required_total_points = 5
|
||||
malefit = -1
|
||||
category = "Symptom (+)"
|
||||
|
||||
/datum/disease_ability/symptom/powerful
|
||||
cost = 4
|
||||
required_total_points = 16
|
||||
required_total_points = 10
|
||||
category = "Symptom (Strong)"
|
||||
|
||||
/datum/disease_ability/symptom/powerful/heal
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
viable_mobtypes = list(/mob/living/carbon/human)
|
||||
mutable = FALSE
|
||||
var/mob/camera/disease/overmind
|
||||
infectable_biotypes = MOB_ORGANIC|MOB_ROBOTIC
|
||||
|
||||
/datum/disease/advance/sentient_disease/New()
|
||||
..()
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
to_chat(owner, "<span class='boldannounce'>You are the Heretic!</span><br>\
|
||||
<B>The old ones gave you these tasks to fulfill:</B>")
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, "<span class='cult'>The book whispers, the forbidden knowledge walks once again!<br>\
|
||||
Your book allows you to research abilities, but be careful, you cannot undo what has been done.<br>\
|
||||
You gain charges by either collecting influences or sacrificing people tracked by the living heart<br> \
|
||||
to_chat(owner, "<span class='cult'>The book whispers softly, its forbidden knowledge walks this plane once again!<br>\
|
||||
Your book allows you to research abilities. Read it very carefully, for you cannot undo what has been done!<br>\
|
||||
You gain charges by either collecting influences or sacrificing people tracked by the living heart.<br> \
|
||||
You can find a basic guide at : https://tgstation13.org/wiki/Heresy_101 </span>")
|
||||
|
||||
/datum/antagonist/heretic/on_gain()
|
||||
@@ -39,7 +39,6 @@
|
||||
gain_knowledge(/datum/eldritch_knowledge/spell/basic)
|
||||
gain_knowledge(/datum/eldritch_knowledge/living_heart)
|
||||
gain_knowledge(/datum/eldritch_knowledge/codex_cicatrix)
|
||||
gain_knowledge(/datum/eldritch_knowledge/eldritch_blade)
|
||||
current.log_message("has been converted to the cult of the forgotten ones!", LOG_ATTACK, color="#960000")
|
||||
GLOB.reality_smash_track.AddMind(owner)
|
||||
START_PROCESSING(SSprocessing,src)
|
||||
@@ -59,6 +58,8 @@
|
||||
GLOB.reality_smash_track.RemoveMind(owner)
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
|
||||
on_death()
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -90,15 +91,25 @@
|
||||
|
||||
/datum/antagonist/heretic/process()
|
||||
|
||||
if(owner.current.stat == DEAD)
|
||||
return
|
||||
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
EK.on_life(owner.current)
|
||||
|
||||
///What happens to the heretic once he dies, used to remove any custom perks
|
||||
/datum/antagonist/heretic/proc/on_death()
|
||||
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
EK.on_death(owner.current)
|
||||
|
||||
/datum/antagonist/heretic/proc/forge_primary_objectives()
|
||||
var/list/assasination = list()
|
||||
var/list/protection = list()
|
||||
for(var/i in 1 to 2)
|
||||
var/pck = pick("assasinate","protect")
|
||||
var/pck = pick("assasinate")
|
||||
switch(pck)
|
||||
if("assasinate")
|
||||
var/datum/objective/assassinate/once/A = new
|
||||
@@ -107,13 +118,6 @@
|
||||
A.find_target(owners,protection)
|
||||
assasination += A.target
|
||||
objectives += A
|
||||
if("protect")
|
||||
var/datum/objective/protect/P = new
|
||||
P.owner = owner
|
||||
var/list/owners = P.get_owners()
|
||||
P.find_target(owners,assasination)
|
||||
protection += P.target
|
||||
objectives += P
|
||||
|
||||
var/datum/objective/sacrifice_ecult/SE = new
|
||||
SE.owner = owner
|
||||
@@ -126,7 +130,7 @@
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
add_antag_hud(antag_hud_type, antag_hud_name, current)
|
||||
handle_clown_mutation(current, mob_override ? null : "Knowledge described in the book allowed you to overcome your clownish nature, allowing you to use complex items effectively.")
|
||||
handle_clown_mutation(current, mob_override ? null : "Ancient knowledge described in the book allows you to overcome your clownish nature, allowing you to use complex items effectively.")
|
||||
current.faction |= "heretics"
|
||||
|
||||
/datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override)
|
||||
@@ -161,7 +165,7 @@
|
||||
cultiewin = FALSE
|
||||
count++
|
||||
if(ascended)
|
||||
parts += "<span class='greentext big'>HERETIC HAS ASCENDED!</span>"
|
||||
parts += "<span class='greentext big'>THE HERETIC ASCENDED!</span>"
|
||||
else
|
||||
if(cultiewin)
|
||||
parts += "<span class='greentext'>The heretic was successful!</span>"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/item/forbidden_book
|
||||
name = "Codex Cicatrix"
|
||||
desc = "Book describing the secrets of the veil."
|
||||
desc = "This book describes the secrets of the veil between worlds."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "codex"
|
||||
item_state = "codex"
|
||||
@@ -74,8 +74,8 @@
|
||||
last_user = user
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
icon_state = "book_open"
|
||||
flick("book_opening", src)
|
||||
icon_state = "codex_open"
|
||||
flick("codex_opening", src)
|
||||
ui = new(user, src, "ForbiddenLore", name)
|
||||
ui.open()
|
||||
|
||||
@@ -133,13 +133,13 @@
|
||||
if(initial(EK.name) != ekname)
|
||||
continue
|
||||
if(cultie.gain_knowledge(EK))
|
||||
charge -= text2num(params["cost"])
|
||||
charge -= initial(EK.cost)
|
||||
return TRUE
|
||||
|
||||
update_icon() // Not applicable to all objects.
|
||||
|
||||
/obj/item/forbidden_book/ui_close(mob/user)
|
||||
flick("book_closing",src)
|
||||
flick("codex_closing",src)
|
||||
icon_state = initial(icon_state)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/effect/eldritch
|
||||
name = "Generic rune"
|
||||
desc = "Weird combination of shapes and symbols etched into the floor itself. The indentation is filled with thick black tar-like fluid."
|
||||
desc = "A flowing circle of shapes and runes is etched into the floor, filled with a thick black tar-like fluid."
|
||||
anchored = TRUE
|
||||
icon_state = ""
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
@@ -14,7 +14,7 @@
|
||||
I.override = TRUE
|
||||
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "heretic_rune", I)
|
||||
|
||||
/obj/effect/eldritch/attack_hand(mob/living/user)
|
||||
/obj/effect/eldritch/attack_hand(mob/living/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
@@ -81,7 +81,7 @@
|
||||
continue
|
||||
|
||||
flick("[icon_state]_active",src)
|
||||
playsound(user, 'sound/magic/castsummon.ogg', 75, TRUE)
|
||||
playsound(user, 'sound/magic/castsummon.ogg', 75, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_exponent = 10)
|
||||
//we are doing this since some on_finished_recipe subtract the atoms from selected_atoms making them invisible permanently.
|
||||
var/list/atoms_to_disappear = selected_atoms.Copy()
|
||||
for(var/to_disappear in atoms_to_disappear)
|
||||
@@ -90,31 +90,31 @@
|
||||
atom_to_disappear.invisibility = INVISIBILITY_ABSTRACT
|
||||
if(current_eldritch_knowledge.on_finished_recipe(user,selected_atoms,loc))
|
||||
current_eldritch_knowledge.cleanup_atoms(selected_atoms)
|
||||
is_in_use = FALSE
|
||||
|
||||
for(var/to_appear in atoms_to_disappear)
|
||||
var/atom/atom_to_appear = to_appear
|
||||
//we need to reappear the item just in case the ritual didnt consume everything... or something.
|
||||
atom_to_appear.invisibility = initial(atom_to_appear.invisibility)
|
||||
|
||||
is_in_use = FALSE
|
||||
return
|
||||
is_in_use = FALSE
|
||||
to_chat(user,"<span class='warning'>Your ritual failed! You used either wrong components or are missing something important!</span>")
|
||||
to_chat(user,"<span class='warning'>Your ritual failed! You either used the wrong components or are missing something important!</span>")
|
||||
|
||||
/obj/effect/eldritch/big
|
||||
name = "transmutation circle"
|
||||
name = "transmutation rune"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "eldritch_rune1"
|
||||
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
|
||||
pixel_y = -32
|
||||
|
||||
/**
|
||||
* #Reality smash tracker
|
||||
*
|
||||
* Stupid fucking list holder, DONT create new ones, it will break the game, this is automnatically created whenever eldritch cultists are created.
|
||||
*
|
||||
* Tracks relevant data, generates relevant data, useful tool
|
||||
*/
|
||||
* #Reality smash tracker
|
||||
*
|
||||
* Stupid fucking list holder, DONT create new ones, it will break the game, this is automnatically created whenever eldritch cultists are created.
|
||||
*
|
||||
* Tracks relevant data, generates relevant data, useful tool
|
||||
*/
|
||||
/datum/reality_smash_tracker
|
||||
///list of tracked reality smashes
|
||||
var/list/smashes = list()
|
||||
@@ -127,12 +127,11 @@
|
||||
QDEL_LIST(smashes)
|
||||
targets.Cut()
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Automatically fixes the target and smash network
|
||||
*
|
||||
* Fixes any bugs that are caused by late Generate() or exchanging clients
|
||||
*/
|
||||
* Automatically fixes the target and smash network
|
||||
*
|
||||
* Fixes any bugs that are caused by late Generate() or exchanging clients
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/ReworkNetwork()
|
||||
listclearnulls(smashes)
|
||||
for(var/mind in targets)
|
||||
@@ -144,52 +143,51 @@
|
||||
reality_smash.AddMind(mind)
|
||||
|
||||
/**
|
||||
* Generates a set amount of reality smashes based on the N value
|
||||
*
|
||||
* Automatically creates more reality smashes
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/_Generate()
|
||||
* Generates a set amount of reality smashes based on the N value
|
||||
*
|
||||
* Automatically creates more reality smashes
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/Generate(mob/caller)
|
||||
if(istype(caller))
|
||||
targets += caller
|
||||
var/targ_len = length(targets)
|
||||
var/smash_len = length(smashes)
|
||||
var/number = targ_len * 6 - smash_len
|
||||
var/number = max(targ_len * (6-(targ_len-1)) - smash_len,1)
|
||||
|
||||
for(var/i in 0 to number)
|
||||
|
||||
var/turf/chosen_location = get_safe_random_station_turf()
|
||||
|
||||
//we also dont want them close to each other, at least 1 tile of seperation
|
||||
var/obj/effect/reality_smash/what_if_i_have_one = locate() in range(1, chosen_location)
|
||||
var/obj/effect/broken_illusion/what_if_i_had_one_but_got_used = locate() in range(1, chosen_location)
|
||||
if(what_if_i_have_one || what_if_i_had_one_but_got_used) //we dont want to spawn
|
||||
continue
|
||||
var/obj/effect/reality_smash/RS = new/obj/effect/reality_smash(chosen_location)
|
||||
smashes += RS
|
||||
new /obj/effect/reality_smash(chosen_location)
|
||||
ReworkNetwork()
|
||||
|
||||
|
||||
/**
|
||||
* Adds a mind to the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to add someone to the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/AddMind(var/datum/mind/M)
|
||||
RegisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN,.proc/ReworkNetwork)
|
||||
targets |= M
|
||||
_Generate()
|
||||
for(var/X in smashes)
|
||||
var/obj/effect/reality_smash/reality_smash = X
|
||||
reality_smash.AddMind(M)
|
||||
* Adds a mind to the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to add someone to the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/AddMind(datum/mind/e_cultists)
|
||||
RegisterSignal(e_cultists.current,COMSIG_MOB_CLIENT_LOGIN,.proc/ReworkNetwork)
|
||||
targets |= e_cultists
|
||||
Generate()
|
||||
for(var/obj/effect/reality_smash/reality_smash in smashes)
|
||||
reality_smash.AddMind(e_cultists)
|
||||
|
||||
|
||||
/**
|
||||
* Removes a mind from the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to remove someone from the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/RemoveMind(var/datum/mind/M)
|
||||
UnregisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN)
|
||||
targets -= M
|
||||
for(var/obj/effect/reality_smash/RS in smashes)
|
||||
RS.RemoveMind(M)
|
||||
* Removes a mind from the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to remove someone from the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/RemoveMind(datum/mind/e_cultists)
|
||||
UnregisterSignal(e_cultists.current,COMSIG_MOB_CLIENT_LOGIN)
|
||||
targets -= e_cultists
|
||||
for(var/obj/effect/reality_smash/reality_smash in smashes)
|
||||
reality_smash.RemoveMind(e_cultists)
|
||||
|
||||
/obj/effect/broken_illusion
|
||||
name = "pierced reality"
|
||||
@@ -198,65 +196,67 @@
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
alpha = 0
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
|
||||
/obj/effect/broken_illusion/Initialize()
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, .proc/show_presence), 15 SECONDS)
|
||||
var/image/I = image(icon = 'icons/effects/eldritch.dmi', icon_state = null, loc = src)
|
||||
addtimer(CALLBACK(src,.proc/show_presence),15 SECONDS)
|
||||
|
||||
var/image/I = image('icons/effects/eldritch.dmi',src,null,OBJ_LAYER)
|
||||
I.override = TRUE
|
||||
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "pierced_reality", I)
|
||||
|
||||
///Makes this obj appear out of nothing
|
||||
/obj/effect/broken_illusion/proc/show_presence()
|
||||
invisibility = 0
|
||||
animate(src, alpha = 255, time = 15 SECONDS)
|
||||
animate(src,alpha = 255,time = 15 SECONDS)
|
||||
|
||||
/obj/effect/broken_illusion/attack_hand(mob/living/user)
|
||||
/obj/effect/broken_illusion/attack_hand(mob/living/user, list/modifiers)
|
||||
if(!ishuman(user))
|
||||
return ..()
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(IS_HERETIC(human_user))
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control!</span>")
|
||||
else
|
||||
var/obj/item/bodypart/arm = human_user.get_active_hand()
|
||||
if(prob(25))
|
||||
to_chat(human_user,"<span class='userdanger'>An otherwordly presence tears your arm apart into atoms as you try to touch the hole in the very fabric of reality!</span>")
|
||||
to_chat(human_user,"<span class='userdanger'>An otherwordly presence tears and atomizes your arm as you try to touch the hole in the very fabric of reality!</span>")
|
||||
arm.dismember()
|
||||
qdel(arm)
|
||||
else
|
||||
to_chat(human_user,"<span class='danger'>You pull your hand away from the hole as eldritch energy flails out, trying to latch onto existence itself!</span>")
|
||||
to_chat(human_user,"<span class='danger'>You pull your hand away from the hole as the eldritch energy flails trying to latch onto existance itself!</span>")
|
||||
|
||||
|
||||
/obj/effect/broken_illusion/attack_tk(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(IS_HERETIC(human_user))
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control!</span>")
|
||||
return
|
||||
//a very elaborate way to suicide
|
||||
to_chat(human_user,"<span class='userdanger'>Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!</span>")
|
||||
human_user.ghostize()
|
||||
var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
|
||||
if(head)
|
||||
head.dismember()
|
||||
qdel(head)
|
||||
else
|
||||
//a very elaborate way to suicide
|
||||
to_chat(human_user,"<span class='userdanger'>Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!</span>")
|
||||
human_user.ghostize()
|
||||
var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
|
||||
if(head)
|
||||
head.dismember()
|
||||
qdel(head)
|
||||
else
|
||||
human_user.gib()
|
||||
human_user.gib()
|
||||
|
||||
var/datum/effect_system/reagents_explosion/explosion = new()
|
||||
explosion.set_up(1, get_turf(human_user), TRUE, 0)
|
||||
explosion.start()
|
||||
|
||||
var/datum/effect_system/reagents_explosion/explosion = new()
|
||||
explosion.set_up(1, get_turf(human_user), 1, 0)
|
||||
explosion.start()
|
||||
|
||||
/obj/effect/broken_illusion/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_HERETIC(user) && ishuman(user))
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
to_chat(human_user,"<span class='userdanger'>Your brain hurts when you look at this!</span>")
|
||||
human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,20,190)
|
||||
to_chat(human_user,"<span class='warning'>Your mind burns as you stare at the tear!</span>")
|
||||
human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,10,190)
|
||||
SEND_SIGNAL(human_user, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
|
||||
/obj/effect/reality_smash
|
||||
name = "/improper reality smash"
|
||||
name = "reality smash"
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
@@ -270,44 +270,41 @@
|
||||
|
||||
/obj/effect/reality_smash/Initialize()
|
||||
. = ..()
|
||||
GLOB.reality_smash_track.smashes += src
|
||||
img = image(icon, src, image_state, OBJ_LAYER)
|
||||
generate_name()
|
||||
|
||||
/obj/effect/reality_smash/Destroy()
|
||||
GLOB.reality_smash_track.smashes -= src
|
||||
on_destroy()
|
||||
return ..()
|
||||
|
||||
///Custom effect that happens on destruction
|
||||
/obj/effect/reality_smash/proc/on_destroy()
|
||||
for(var/cm in minds)
|
||||
var/datum/mind/cultie = cm
|
||||
if(cultie.current?.client)
|
||||
cultie.current.client.images -= img
|
||||
for(var/e_cultists in minds)
|
||||
var/datum/mind/e_cultie = e_cultists
|
||||
if(e_cultie.current?.client)
|
||||
e_cultie.current.client.images -= img
|
||||
//clear the list
|
||||
minds -= cultie
|
||||
GLOB.reality_smash_track.smashes -= src
|
||||
minds -= e_cultie
|
||||
img = null
|
||||
new /obj/effect/broken_illusion(drop_location())
|
||||
var/obj/effect/broken_illusion/illusion = new /obj/effect/broken_illusion(drop_location())
|
||||
illusion.name = pick("Researched","Siphoned","Analyzed","Emptied","Drained") + " " + name
|
||||
|
||||
///Makes the mind able to see this effect
|
||||
/obj/effect/reality_smash/proc/AddMind(var/datum/mind/cultie)
|
||||
minds |= cultie
|
||||
if(cultie.current.client)
|
||||
cultie.current.client.images |= img
|
||||
|
||||
|
||||
/obj/effect/reality_smash/proc/AddMind(datum/mind/e_cultie)
|
||||
minds |= e_cultie
|
||||
if(e_cultie.current.client)
|
||||
e_cultie.current.client.images |= img
|
||||
|
||||
///Makes the mind not able to see this effect
|
||||
/obj/effect/reality_smash/proc/RemoveMind(var/datum/mind/cultie)
|
||||
minds -= cultie
|
||||
if(cultie.current.client)
|
||||
cultie.current.client.images -= img
|
||||
|
||||
|
||||
/obj/effect/reality_smash/proc/RemoveMind(datum/mind/e_cultie)
|
||||
minds -= e_cultie
|
||||
if(e_cultie.current.client)
|
||||
e_cultie.current.client.images -= img
|
||||
|
||||
///Generates random name
|
||||
/obj/effect/reality_smash/proc/generate_name()
|
||||
var/static/list/prefix = list("Omniscient","Thundering","Enlightening","Intrusive","Rejectful","Atomized","Subtle","Rising","Lowering","Fleeting","Towering","Blissful","Arrogant","Threatening","Peaceful","Aggressive")
|
||||
var/static/list/postfix = list("Flaw","Presence","Crack","Heat","Cold","Memory","Reminder","Breeze","Grasp","Sight","Whisper","Flow","Touch","Veil","Thought","Imperfection","Blemish","Blush")
|
||||
|
||||
name = pick(prefix) + " " + pick(postfix)
|
||||
name = "\improper" + pick(prefix) + " " + pick(postfix)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/item/living_heart
|
||||
name = "living heart"
|
||||
desc = "Link to the worlds beyond."
|
||||
desc = "A link to the worlds beyond."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "living_heart"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
@@ -35,7 +35,7 @@
|
||||
if(0 to 15)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is near you. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(16 to 31)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!</span>")
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinity. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(32 to 127)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!</span>")
|
||||
else
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
/datum/action/innate/heretic_shatter
|
||||
name = "Shattering Offer"
|
||||
desc = "By breaking your blade, you will be granted salvation from a dire situation. (Teleports you to a random safe turf on your current z level, but destroys your blade.)"
|
||||
desc = "After a brief delay, you will be granted salvation from a dire situation at the cost of your blade. (Teleports you to a random safe turf on your current z level after a windup, but destroys your blade.)"
|
||||
background_icon_state = "bg_ecult"
|
||||
button_icon_state = "shatter"
|
||||
icon_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
@@ -67,13 +67,14 @@
|
||||
return FALSE
|
||||
|
||||
/datum/action/innate/heretic_shatter/Activate()
|
||||
var/turf/safe_turf = find_safe_turf(zlevels = sword.z, extended_safety_checks = TRUE)
|
||||
do_teleport(holder,safe_turf,forceMove = TRUE)
|
||||
to_chat(holder,"<span class='warning'>You feel a gust of energy flow through your body... the Rusted Hills heard your call...</span>")
|
||||
qdel(sword)
|
||||
if(do_after(holder,10, target = holder))
|
||||
var/turf/safe_turf = find_safe_turf(zlevels = sword.z, extended_safety_checks = TRUE)
|
||||
do_teleport(holder,safe_turf,forceMove = TRUE,channel=TELEPORT_CHANNEL_MAGIC)
|
||||
to_chat(holder,"<span class='warning'>You feel a gust of energy flow through your body... the Rusted Hills heard your call...</span>")
|
||||
qdel(sword)
|
||||
|
||||
/obj/item/melee/sickly_blade
|
||||
name = "eldritch blade"
|
||||
name = "sickly blade"
|
||||
desc = "A sickly green crescent blade, decorated with an ornamental eye. You feel like you're being watched..."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eldritch_blade"
|
||||
@@ -95,12 +96,17 @@
|
||||
. = ..()
|
||||
linked_action = new(src)
|
||||
|
||||
/obj/item/melee/sickly_blade/attack(mob/living/M, mob/living/user)
|
||||
if(!(IS_HERETIC(user) || !IS_HERETIC_MONSTER(user)))
|
||||
to_chat(user,"<span class='danger'>You feel a pulse of some alien intellect lash out at your mind!</span>")
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
human_user.AdjustParalyzed(5 SECONDS)
|
||||
return FALSE
|
||||
/obj/item/melee/sickly_blade/attack(mob/living/target, mob/living/user)
|
||||
if(!(IS_HERETIC(user) || IS_HERETIC_MONSTER(user)))
|
||||
to_chat(user,"<span class='danger'>You feel a pulse of alien intellect lash out at your mind!</span>")
|
||||
user.DefaultCombatKnockdown(100)
|
||||
user.dropItemToGround(src, TRUE)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.apply_damage(rand(force/2, force), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
|
||||
else
|
||||
user.adjustBruteLoss(rand(force/2,force))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/melee/sickly_blade/pickup(mob/user)
|
||||
@@ -114,20 +120,22 @@
|
||||
/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
if(!cultie || !proximity_flag)
|
||||
if(!cultie)
|
||||
return
|
||||
var/list/knowledge = cultie.get_all_knowledge()
|
||||
for(var/X in knowledge)
|
||||
var/datum/eldritch_knowledge/eldritch_knowledge_datum = knowledge[X]
|
||||
eldritch_knowledge_datum.on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
if(proximity_flag)
|
||||
eldritch_knowledge_datum.on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
else
|
||||
eldritch_knowledge_datum.on_ranged_attack_eldritch_blade(target,user,click_parameters)
|
||||
|
||||
/obj/item/melee/sickly_blade/rust
|
||||
name = "rusted blade"
|
||||
desc = "This crescent blade is decrepit, wasting to dust. Yet still it bites, catching flesh with jagged, rotten teeth."
|
||||
desc = "This crescent blade is decrepit, wasting to rust. Yet still it bites, ripping flesh and bone with jagged, rotten teeth."
|
||||
icon_state = "rust_blade"
|
||||
item_state = "rust_blade"
|
||||
embedding = list("pain_mult" = 4, "embed_chance" = 75, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
|
||||
throwforce = 17
|
||||
|
||||
/obj/item/melee/sickly_blade/ash
|
||||
name = "ashen blade"
|
||||
@@ -138,15 +146,22 @@
|
||||
|
||||
/obj/item/melee/sickly_blade/flesh
|
||||
name = "flesh blade"
|
||||
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins."
|
||||
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the suffering it has endured from its dreadful origins."
|
||||
icon_state = "flesh_blade"
|
||||
item_state = "flesh_blade"
|
||||
wound_bonus = 10
|
||||
bare_wound_bonus = 20
|
||||
|
||||
/obj/item/melee/sickly_blade/void
|
||||
name = "void blade"
|
||||
desc = "Devoid of any substance, this blade reflects nothingness. It is a real depiction of purity, and chaos that ensues after its implementation."
|
||||
icon_state = "void_blade"
|
||||
item_state = "void_blade"
|
||||
throwforce = 25
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet
|
||||
name = "warm eldritch medallion"
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulse of a thousand others."
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulsing of a thousand others."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eye_medalion"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
@@ -184,7 +199,7 @@
|
||||
item_state = "eldritch_armor"
|
||||
flags_inv = HIDESHOES|HIDEJUMPSUIT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS
|
||||
allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book)
|
||||
allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book, /obj/item/living_heart)
|
||||
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
|
||||
// slightly better than normal cult robes
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50,"energy" = 50, "bomb" = 35, "bio" = 20, "rad" = 0, "fire" = 20, "acid" = 20)
|
||||
@@ -192,7 +207,242 @@
|
||||
|
||||
/obj/item/reagent_containers/glass/beaker/eldritch
|
||||
name = "flask of eldritch essence"
|
||||
desc = "Toxic to the close minded. Healing to those with knowledge of the beyond."
|
||||
desc = "Toxic to the closed minded, yet refreshing to those with knowledge of the beyond."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eldrich_flask"
|
||||
list_reagents = list(/datum/reagent/eldritch = 50)
|
||||
|
||||
/obj/item/clothing/head/hooded/cult_hoodie/void
|
||||
name = "void hood"
|
||||
icon_state = "void_cloak"
|
||||
flags_inv = NONE
|
||||
flags_cover = NONE
|
||||
desc = "Black like tar, doesn't reflect any light. Runic symbols line the outside, with each flash you lose comprehension of what you are seeing."
|
||||
item_flags = EXAMINE_SKIP
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void
|
||||
name = "void cloak"
|
||||
desc = "Black like tar, doesn't reflect any light. Runic symbols line the outside, with each flash you loose comprehension of what you are seeing."
|
||||
icon_state = "void_cloak"
|
||||
item_state = "void_cloak"
|
||||
allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book, /obj/item/living_heart)
|
||||
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/void
|
||||
flags_inv = NONE
|
||||
// slightly worse than normal cult robes
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
|
||||
pocket_storage_component_path = /datum/component/storage/concrete/pockets/void_cloak
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/ToggleHood()
|
||||
if(!iscarbon(loc))
|
||||
return
|
||||
var/mob/living/carbon/carbon_user = loc
|
||||
if(IS_HERETIC(carbon_user) || IS_HERETIC_MONSTER(carbon_user))
|
||||
. = ..()
|
||||
//We need to account for the hood shenanigans, and that way we can make sure items always fit, even if one of the slots is used by the fucking hood.
|
||||
if(suittoggled)
|
||||
to_chat(carbon_user,"<span class='notice'>The light shifts around you making the cloak invisible!</span>")
|
||||
else
|
||||
to_chat(carbon_user,"<span class='notice'>The kaleidoscope of colours collapses around you, as the cloak shifts to visibility!</span>")
|
||||
item_flags = suittoggled ? EXAMINE_SKIP : ~EXAMINE_SKIP
|
||||
else
|
||||
to_chat(carbon_user,"<span class='danger'>You can't force the hood onto your head!</span>")
|
||||
|
||||
/obj/item/clothing/mask/void_mask
|
||||
name = "abyssal mask"
|
||||
desc = "Mask created from the suffering of existance, you can look down it's eyes, and notice something gazing back at you."
|
||||
icon_state = "mad_mask"
|
||||
item_state = "mad_mask"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
flags_inv = HIDEFACE|HIDEFACIALHAIR
|
||||
///Who is wearing this
|
||||
var/mob/living/carbon/human/local_user
|
||||
|
||||
/obj/item/clothing/mask/void_mask/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(ishuman(user) && user.mind && slot == SLOT_WEAR_MASK)
|
||||
local_user = user
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
if(IS_HERETIC(user) || IS_HERETIC_MONSTER(user))
|
||||
return
|
||||
ADD_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
|
||||
|
||||
/obj/item/clothing/mask/void_mask/dropped(mob/M)
|
||||
local_user = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/mask/void_mask/process(delta_time)
|
||||
if(!local_user)
|
||||
return PROCESS_KILL
|
||||
|
||||
if((IS_HERETIC(local_user) || IS_HERETIC_MONSTER(local_user)) && HAS_TRAIT(src,TRAIT_NODROP))
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
|
||||
|
||||
for(var/mob/living/carbon/human/human_in_range in spiral_range(9,local_user))
|
||||
if(IS_HERETIC(human_in_range) || IS_HERETIC_MONSTER(human_in_range))
|
||||
continue
|
||||
|
||||
SEND_SIGNAL(human_in_range,COMSIG_VOID_MASK_ACT,rand(-2,-20)*delta_time)
|
||||
|
||||
if(DT_PROB(60,delta_time))
|
||||
human_in_range.hallucination += 5
|
||||
|
||||
if(DT_PROB(40,delta_time))
|
||||
human_in_range.Jitter(5)
|
||||
|
||||
if(DT_PROB(30,delta_time))
|
||||
human_in_range.emote(pick("giggle","laugh"))
|
||||
human_in_range.adjustStaminaLoss(6)
|
||||
|
||||
if(DT_PROB(25,delta_time))
|
||||
human_in_range.Dizzy(5)
|
||||
|
||||
/obj/item/melee/rune_knife
|
||||
name = "\improper Carving Knife"
|
||||
desc = "Cold steel, pure, perfect, this knife can carve the floor in many ways, but only few can evoke the dangers that lurk beneath reality."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "rune_carver"
|
||||
flags_1 = CONDUCT_1
|
||||
sharpness = SHARP_EDGED
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
wound_bonus = 20
|
||||
force = 10
|
||||
throwforce = 20
|
||||
embedding = list(embed_chance=75, jostle_chance=2, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=3, jostle_pain_mult=5, rip_time=15)
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "rended")
|
||||
///turfs that you cannot draw carvings on
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava))
|
||||
///A check to see if you are in process of drawing a rune
|
||||
var/drawing = FALSE
|
||||
///A list of current runes
|
||||
var/list/current_runes = list()
|
||||
///Max amount of runes
|
||||
var/max_rune_amt = 3
|
||||
///Linked action
|
||||
var/datum/action/innate/rune_shatter/linked_action
|
||||
|
||||
/obj/item/melee/rune_knife/examine(mob/user)
|
||||
. = ..()
|
||||
. += "This item can carve 'Alert carving' - nearly invisible rune that when stepped on gives you a prompt about where someone stood on it and who it was, doesn't get destroyed by being stepped on."
|
||||
. += "This item can carve 'Grasping carving' - when stepped on it causes heavy damage to the legs and stuns for 5 seconds."
|
||||
. += "This item can carve 'Mad carving' - when stepped on it causes dizzyness, jiterryness, temporary blindness, confusion , stuttering and slurring."
|
||||
|
||||
/obj/item/melee/rune_knife/Initialize()
|
||||
. = ..()
|
||||
linked_action = new(src)
|
||||
|
||||
/obj/item/melee/rune_knife/pickup(mob/user)
|
||||
. = ..()
|
||||
linked_action.Grant(user, src)
|
||||
|
||||
/obj/item/melee/rune_knife/dropped(mob/user, silent)
|
||||
. = ..()
|
||||
linked_action.Remove(user, src)
|
||||
|
||||
/obj/item/melee/rune_knife/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!is_type_in_typecache(target,blacklisted_turfs) && !drawing && proximity_flag)
|
||||
carve_rune(target,user,proximity_flag,click_parameters)
|
||||
|
||||
///Action of carving runes, gives you the ability to click on floor and choose a rune of your need.
|
||||
/obj/item/melee/rune_knife/proc/carve_rune(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
var/obj/structure/trap/eldritch/elder = locate() in range(1,target)
|
||||
if(elder)
|
||||
to_chat(user,"<span class='notice'>You can't draw runes that close to each other!</span>")
|
||||
return
|
||||
|
||||
for(var/X in current_runes)
|
||||
var/obj/structure/trap/eldritch/eldritch = X
|
||||
if(QDELETED(eldritch) || !eldritch)
|
||||
current_runes -= eldritch
|
||||
|
||||
if(current_runes.len >= max_rune_amt)
|
||||
to_chat(user,"<span class='notice'>The blade cannot support more runes!</span>")
|
||||
return
|
||||
|
||||
var/list/pick_list = list()
|
||||
for(var/E in subtypesof(/obj/structure/trap/eldritch))
|
||||
var/obj/structure/trap/eldritch/eldritch = E
|
||||
pick_list[initial(eldritch.name)] = eldritch
|
||||
|
||||
drawing = TRUE
|
||||
|
||||
var/type = pick_list[input(user,"Choose the rune","Rune") as null|anything in pick_list ]
|
||||
if(!type)
|
||||
drawing = FALSE
|
||||
return
|
||||
|
||||
|
||||
to_chat(user,"<span class='notice'>You start drawing the rune...</span>")
|
||||
if(!do_after(user,5 SECONDS,target = target))
|
||||
drawing = FALSE
|
||||
return
|
||||
|
||||
drawing = FALSE
|
||||
var/obj/structure/trap/eldritch/eldritch = new type(target)
|
||||
eldritch.set_owner(user)
|
||||
current_runes += eldritch
|
||||
|
||||
/datum/action/innate/rune_shatter
|
||||
name = "Rune Break"
|
||||
desc = "Destroys all runes that were drawn by this blade."
|
||||
background_icon_state = "bg_ecult"
|
||||
button_icon_state = "rune_break"
|
||||
icon_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
///Reference to the rune knife it is inside of
|
||||
var/obj/item/melee/rune_knife/sword
|
||||
|
||||
/datum/action/innate/rune_shatter/Grant(mob/user, obj/object)
|
||||
sword = object
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/rune_shatter/Activate()
|
||||
for(var/X in sword.current_runes)
|
||||
var/obj/structure/trap/eldritch/eldritch = X
|
||||
if(!QDELETED(eldritch) && eldritch)
|
||||
qdel(eldritch)
|
||||
|
||||
/obj/item/eldritch_potion
|
||||
name = "Brew of Day and Night"
|
||||
desc = "You should never see this"
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
///Typepath to the status effect this is supposed to hold
|
||||
var/status_effect
|
||||
|
||||
/obj/item/eldritch_potion/attack_self(mob/user)
|
||||
. = ..()
|
||||
to_chat(user,"<span class='notice'>You drink the potion and with the viscous liquid, the glass dematerializes.</span>")
|
||||
effect(user)
|
||||
qdel(src)
|
||||
|
||||
///The effect of the potion if it has any special one, in general try not to override this and utilize the status_effect var to make custom effects.
|
||||
/obj/item/eldritch_potion/proc/effect(mob/user)
|
||||
if(!iscarbon(user))
|
||||
return
|
||||
var/mob/living/carbon/carbie = user
|
||||
carbie.apply_status_effect(status_effect)
|
||||
|
||||
/obj/item/eldritch_potion/crucible_soul
|
||||
name = "Brew of Crucible Soul"
|
||||
desc = "Allows you to phase through walls for 15 seconds, after the time runs out, you get teleported to your original location."
|
||||
icon_state = "crucible_soul"
|
||||
status_effect = /datum/status_effect/crucible_soul
|
||||
|
||||
/obj/item/eldritch_potion/duskndawn
|
||||
name = "Brew of Dusk and Dawn"
|
||||
desc = "Allows you to see clearly through walls and objects for 60 seconds."
|
||||
icon_state = "clarity"
|
||||
status_effect = /datum/status_effect/duskndawn
|
||||
|
||||
/obj/item/eldritch_potion/wounded
|
||||
name = "Brew of Wounded Soldier"
|
||||
desc = "For the next 60 seconds each wound will heal you, minor wounds heal 1 of it's damage type per second, moderate heal 3 and critical heal 6. You also become immune to damage slowdon."
|
||||
icon_state = "marshal"
|
||||
status_effect = /datum/status_effect/marshal
|
||||
|
||||
@@ -65,6 +65,14 @@
|
||||
/datum/eldritch_knowledge/proc/recipe_snowflake_check(list/atoms,loc)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* A proc that handles the code when the mob dies
|
||||
*
|
||||
* This proc is primarily used to end any soundloops when the heretic dies
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_death(mob/user)
|
||||
return
|
||||
|
||||
/**
|
||||
* What happens once the recipe is succesfully finished
|
||||
*
|
||||
@@ -100,7 +108,6 @@
|
||||
/datum/eldritch_knowledge/proc/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
return FALSE
|
||||
|
||||
|
||||
/**
|
||||
* Sickly blade act
|
||||
*
|
||||
@@ -109,6 +116,14 @@
|
||||
/datum/eldritch_knowledge/proc/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
return
|
||||
|
||||
/**
|
||||
* Sickly blade distant act
|
||||
*
|
||||
* Same as [/datum/eldritch_knowledge/proc/on_eldritch_blade] but works on targets that are not in proximity to you.
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_ranged_attack_eldritch_blade(atom/target,mob/user,click_parameters)
|
||||
return
|
||||
|
||||
//////////////
|
||||
///Subtypes///
|
||||
//////////////
|
||||
@@ -150,7 +165,7 @@
|
||||
compiled_list[human_to_check.real_name] = human_to_check
|
||||
|
||||
if(compiled_list.len == 0)
|
||||
to_chat(user, "<span class='warning'>The items don't posses required fingerprints.</span>")
|
||||
to_chat(user, "<span class='warning'>These items don't possess the required fingerprints or DNA.</span>")
|
||||
return FALSE
|
||||
|
||||
var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sortList(compiled_list, /proc/cmp_mob_realname_dsc)
|
||||
@@ -222,9 +237,9 @@
|
||||
|
||||
/datum/eldritch_knowledge/spell/basic
|
||||
name = "Break of Dawn"
|
||||
desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune."
|
||||
gain_text = "Gates of Mansus open up to your mind."
|
||||
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/spell/silence)
|
||||
desc = "Starts your journey in the Mansus. Allows you to select a target using a living heart on a transmutation rune."
|
||||
gain_text = "Another day at a meaningless job. You feel a shimmer around you, as a realization of something strange in your backpack unfolds. You look at it, unknowingly opening a new chapter in your life."
|
||||
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/base_void)
|
||||
cost = 0
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
|
||||
required_atoms = list(/obj/item/living_heart)
|
||||
@@ -279,7 +294,7 @@
|
||||
var/datum/mind/targeted = A.find_target(blacklist = target_blacklist)//easy way, i dont feel like copy pasting that entire block of code
|
||||
if(!targeted)
|
||||
break
|
||||
targets[targeted.current.real_name] = targeted.current
|
||||
targets["[targeted.current.real_name] the [targeted.assigned_role]"] = targeted.current
|
||||
LH.target = targets[input(user,"Choose your next target","Target") in targets]
|
||||
|
||||
if(!LH.target && targets.len)
|
||||
@@ -301,7 +316,6 @@
|
||||
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
LH.sac_targetter = EC
|
||||
EC.sac_targetted.Add(LH.target.real_name)
|
||||
|
||||
else
|
||||
to_chat(user,"<span class='warning'>target could not be found for living heart.</span>")
|
||||
|
||||
@@ -311,30 +325,22 @@
|
||||
/datum/eldritch_knowledge/living_heart
|
||||
name = "Living Heart"
|
||||
desc = "Allows you to create additional living hearts, using a heart, a pool of blood and a poppy. Living hearts when used on a transmutation rune will grant you a person to hunt and sacrifice on the rune. Every sacrifice gives you an additional charge in the book."
|
||||
gain_text = "Disconnected, yet it still beats."
|
||||
gain_text = "The Gates of Mansus open up to your mind."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/organ/heart,/obj/effect/decal/cleanable/blood,/obj/item/reagent_containers/food/snacks/grown/poppy)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/silence)
|
||||
result_atoms = list(/obj/item/living_heart)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/codex_cicatrix
|
||||
name = "Codex Cicatrix"
|
||||
desc = "Allows you to create a spare Codex Cicatrix if you have lost one, using a bible, human skin, a pen and a pair of eyes."
|
||||
gain_text = "Their hands are at your throat, yet you see them not."
|
||||
gain_text = "Their hand is at your throat, yet you see Them not."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/stack/sheet/animalhide/human,/obj/item/storage/book/bible,/obj/item/pen)
|
||||
result_atoms = list(/obj/item/forbidden_book)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/eldritch_blade
|
||||
name = "Eldritch Blade"
|
||||
desc = "Allows you to create a sickly, eldritch blade by transmuting a glass shard and a metal rod atop a transmutation rune."
|
||||
gain_text = "The first step starts with sacrifice."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/shard,/obj/item/stack/rods)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/spell/silence
|
||||
name = "Silence"
|
||||
desc = "Allows you to use the power of the Mansus to force an individual's tongue to be held down for up to twenty seconds. They'll notice quickly, however."
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
|
||||
name = "Ashen Passage"
|
||||
desc = "Low range spell allowing you to pass through a few walls."
|
||||
desc = "A short range spell allowing you to pass unimpeded through a few walls."
|
||||
school = "transmutation"
|
||||
invocation = "DULK'ES PRE'ZIMAS"
|
||||
invocation_type = "whisper"
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
|
||||
name = "Mansus Grasp"
|
||||
desc = "Touch spell that allows you to channel the power of the Old Gods through you."
|
||||
desc = "A touch spell that lets you channel the power of the Old Gods through your grip."
|
||||
hand_path = /obj/item/melee/touch_attack/mansus_fist
|
||||
school = "evocation"
|
||||
charge_max = 100
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
/obj/item/melee/touch_attack/mansus_fist
|
||||
name = "Mansus Grasp"
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research."
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown and major stamina damage in addition to some brute. It gains additional beneficial effects as you expand your knowledge of the Mansus."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "mansus_grasp"
|
||||
item_state = "mansus"
|
||||
@@ -50,13 +50,13 @@
|
||||
|
||||
/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
|
||||
if(!proximity_flag || target == user)
|
||||
if(!proximity_flag | target == user)
|
||||
return
|
||||
playsound(user, 'sound/items/welder.ogg', 75, TRUE)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
tar.visible_message("<span class='danger'>The spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
var/datum/mind/M = user.mind
|
||||
var/datum/antagonist/heretic/cultie = M.has_antag_datum(/datum/antagonist/heretic)
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion
|
||||
name = "Aggressive Spread"
|
||||
desc = "Spreads rust onto nearby turfs."
|
||||
desc = "Spreads rust onto nearby surfaces."
|
||||
school = "transmutation"
|
||||
charge_max = 300 //twice as long as mansus grasp
|
||||
clothes_req = FALSE
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small
|
||||
name = "Rust Conversion"
|
||||
desc = "Spreads rust onto nearby turfs."
|
||||
desc = "Spreads rust onto nearby surfaces."
|
||||
range = 2
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/blood_siphon
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/rust_wave
|
||||
name = "Patron's Reach"
|
||||
desc = "Channels energy into your gauntlet - firing it results in a wave of rust being created in it's wake."
|
||||
desc = "Channels energy into your gauntlet- unleashing it creates a wave of rust in its wake."
|
||||
projectile_type = /obj/item/projectile/magic/spell/rust_wave
|
||||
charge_max = 350
|
||||
clothes_req = FALSE
|
||||
@@ -213,7 +213,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave
|
||||
name = "Cleave"
|
||||
desc = "Causes severe bleeding on a target and people around them"
|
||||
desc = "Causes severe bleeding on a target and several targets around them."
|
||||
school = "transmutation"
|
||||
charge_max = 350
|
||||
clothes_req = FALSE
|
||||
@@ -250,7 +250,8 @@
|
||||
var/obj/item/bodypart/bodypart = pick(target.bodyparts)
|
||||
var/datum/wound/slash/critical/crit_wound = new
|
||||
crit_wound.apply_wound(bodypart)
|
||||
target.adjustFireLoss(20)
|
||||
crit_wound.apply_wound(bodypart)
|
||||
target.adjustBruteLoss(20)
|
||||
new /obj/effect/temp_visual/cleave(target.drop_location())
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave/can_target(atom/target, mob/user, silent)
|
||||
@@ -292,7 +293,7 @@
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
tar.visible_message("<span class='danger'>The spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
|
||||
if(iscarbon(target))
|
||||
@@ -331,7 +332,7 @@
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
tar.visible_message("<span class='danger'>The spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
|
||||
if(iscarbon(target))
|
||||
@@ -343,7 +344,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/nightwatchers_rite
|
||||
name = "Nightwatcher's Rite"
|
||||
desc = "Powerful spell that releases 5 streams of fire away from you."
|
||||
desc = "A powerful spell that releases 5 streams of fire away from you."
|
||||
school = "transmutation"
|
||||
invocation = "IGNIS'INTI"
|
||||
invocation_type = "whisper"
|
||||
@@ -390,13 +391,13 @@
|
||||
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L.anti_magic_check())
|
||||
L.visible_message("<span class='danger'>Spell bounces off of [L]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
L.visible_message("<span class='danger'>The spell bounces off of [L]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
continue
|
||||
if(L in hit_list || L == source)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(15)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by a blast of fire!</span>")
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [source]'s eldritch flames!</span>")
|
||||
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
@@ -433,7 +434,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade
|
||||
name = "Fire Cascade"
|
||||
desc = "creates hot turfs around you."
|
||||
desc = "Heats the air around you."
|
||||
school = "transmutation"
|
||||
charge_max = 300 //twice as long as mansus grasp
|
||||
clothes_req = FALSE
|
||||
@@ -470,7 +471,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fire_sworn
|
||||
name = "Oath of Fire"
|
||||
desc = "For a minute you will passively create a ring of fire around you."
|
||||
desc = "For a minute, you will passively create a ring of fire around you."
|
||||
invocation = "IGNIS'AISTRA'LISTRE"
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
@@ -509,7 +510,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/worm_contract
|
||||
name = "Force Contract"
|
||||
desc = "Forces all the worm parts to collapse onto a single turf"
|
||||
desc = "Forces your body to contract onto a single tile."
|
||||
invocation_type = "none"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
@@ -539,7 +540,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fiery_rebirth
|
||||
name = "Nightwatcher's Rebirth"
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a target is in critical condition it drains the last of their vitality, killing them."
|
||||
invocation = "PETHRO'MINO'IGNI"
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
@@ -566,12 +567,12 @@
|
||||
human_user.adjustBruteLoss(-10, FALSE)
|
||||
human_user.adjustFireLoss(-10, FALSE)
|
||||
human_user.adjustStaminaLoss(-10, FALSE)
|
||||
human_user.adjustToxLoss(-10, FALSE)
|
||||
human_user.adjustToxLoss(-10, FALSE, TRUE)
|
||||
human_user.adjustOxyLoss(-10)
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/manse_link
|
||||
name = "Mansus Link"
|
||||
desc = "Piercing through reality, connecting minds. This spell allows you to add people to a mansus net, allowing them to communicate with eachother"
|
||||
desc = "Piercing through reality, connecting minds. This spell allows you to add people to a Mansus Net, allowing them to communicate with each other from afar."
|
||||
school = "transmutation"
|
||||
charge_max = 300
|
||||
clothes_req = FALSE
|
||||
@@ -605,7 +606,7 @@
|
||||
|
||||
/datum/action/innate/mansus_speech
|
||||
name = "Mansus Link"
|
||||
desc = "Send a psychic message to everyone connected to your mansus link."
|
||||
desc = "Send a psychic message to everyone connected to your Mansus Net."
|
||||
button_icon_state = "link_speech"
|
||||
icon_icon = 'icons/mob/actions/actions_slime.dmi'
|
||||
background_icon_state = "bg_ecult"
|
||||
@@ -747,3 +748,219 @@
|
||||
return 3
|
||||
else
|
||||
return 2
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shed_human_form
|
||||
name = "Shed form"
|
||||
desc = "Shed your fragile form, become one with the arms, become one with the emperor."
|
||||
invocation_type = "shout"
|
||||
invocation = "REALITY UNCOIL!"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 100
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "worm_ascend"
|
||||
var/segment_length = 10
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shed_human_form/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
var/mob/living/target = user
|
||||
var/mob/living/mob_inside = locate() in target.contents - target
|
||||
|
||||
if(!mob_inside)
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/prime/outside = new(user.loc,TRUE,segment_length)
|
||||
target.mind.transfer_to(outside, TRUE)
|
||||
target.forceMove(outside)
|
||||
target.apply_status_effect(STATUS_EFFECT_STASIS,STASIS_ASCENSION_EFFECT)
|
||||
for(var/mob/living/carbon/human/humie in view(9,outside)-target)
|
||||
if(IS_HERETIC(humie) || IS_HERETIC_MONSTER(humie))
|
||||
continue
|
||||
SEND_SIGNAL(humie, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
///They see the very reality uncoil before their eyes.
|
||||
if(prob(25))
|
||||
var/trauma = pick(subtypesof(BRAIN_TRAUMA_MILD) + subtypesof(BRAIN_TRAUMA_SEVERE))
|
||||
humie.gain_trauma(new trauma(), TRAUMA_RESILIENCE_LOBOTOMY)
|
||||
return
|
||||
|
||||
if(iscarbon(mob_inside))
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/prime/armsy = target
|
||||
if(mob_inside.remove_status_effect(STATUS_EFFECT_STASIS,STASIS_ASCENSION_EFFECT))
|
||||
mob_inside.forceMove(armsy.loc)
|
||||
armsy.mind.transfer_to(mob_inside, TRUE)
|
||||
segment_length = armsy.get_length()
|
||||
qdel(armsy)
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/void_blink
|
||||
name = "Void Phase"
|
||||
desc = "Let's you blink to your pointed destination, causes 3x3 aoe damage bubble around your pointed destination and your current location. It has a minimum range of 3 tiles and a maximum range of 9 tiles."
|
||||
invocation_type = "whisper"
|
||||
invocation = "PAS'VEIK"
|
||||
clothes_req = FALSE
|
||||
range = 9
|
||||
action_background_icon_state = "bg_ecult"
|
||||
charge_max = 300
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "voidblink"
|
||||
selection_type = "range"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/void_blink/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(get_dist(get_turf(user),get_turf(target)) < 3 )
|
||||
return FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/void_blink/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
var/target = targets[1]
|
||||
var/turf/targeted_turf = get_turf(target)
|
||||
|
||||
playsound(user,'sound/magic/voidblink.ogg',100)
|
||||
playsound(targeted_turf,'sound/magic/voidblink.ogg',100)
|
||||
|
||||
new /obj/effect/temp_visual/voidin(user.drop_location())
|
||||
new /obj/effect/temp_visual/voidout(targeted_turf)
|
||||
|
||||
for(var/mob/living/living_mob in range(1,user)-user)
|
||||
if(IS_HERETIC(living_mob) || IS_HERETIC_MONSTER(living_mob))
|
||||
continue
|
||||
living_mob.adjustBruteLoss(40)
|
||||
|
||||
for(var/mob/living/living_mob in range(1,targeted_turf)-user)
|
||||
if(IS_HERETIC(living_mob) || IS_HERETIC_MONSTER(living_mob))
|
||||
continue
|
||||
living_mob.adjustBruteLoss(40)
|
||||
|
||||
do_teleport(user,targeted_turf,0,TRUE,no_effects = TRUE,channel=TELEPORT_CHANNEL_MAGIC)
|
||||
|
||||
/obj/effect/temp_visual/voidin
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "void_blink_in"
|
||||
alpha = 150
|
||||
duration = 6
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
|
||||
/obj/effect/temp_visual/voidout
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "void_blink_out"
|
||||
alpha = 150
|
||||
duration = 6
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/void_pull
|
||||
name = "Void Pull"
|
||||
desc = "Call the void, this pulls all nearby people closer to you, damages people already around you. If they are 4 tiles or closer they are also knocked down and a micro-stun is applied."
|
||||
invocation_type = "whisper"
|
||||
invocation = "VISA'GALIS TRAUK'IMAS"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 400
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "voidpull"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/void_pull/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
for(var/mob/living/living_mob in range(1,user)-user)
|
||||
if(IS_HERETIC(living_mob) || IS_HERETIC_MONSTER(living_mob))
|
||||
continue
|
||||
living_mob.adjustBruteLoss(30)
|
||||
|
||||
playsound(user,'sound/magic/voidpull.ogg',75)
|
||||
new /obj/effect/temp_visual/voidin(user.drop_location())
|
||||
for(var/mob/living/livies in view(7,user)-user)
|
||||
|
||||
if(get_dist(user,livies) < 4)
|
||||
livies.AdjustKnockdown(3 SECONDS)
|
||||
livies.AdjustParalyzed(0.5 SECONDS)
|
||||
|
||||
for(var/i in 1 to 3)
|
||||
livies.forceMove(get_step_towards(livies,user))
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/boogie_woogie
|
||||
name = "Void's Applause"
|
||||
desc = "Swap positions with someone at the clap of your hands."
|
||||
school = "transmutation"
|
||||
charge_max = 100
|
||||
clothes_req = FALSE
|
||||
invocation = "BOOGIE WOOGIE"
|
||||
invocation_type = "none"
|
||||
range = 10
|
||||
message = "The world around you suddenly shifts!"
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mansus_link"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/boogie_woogie/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
var/target = targets[1]
|
||||
user.emote("clap1")
|
||||
playsound(user, 'sound/magic/voidblink.ogg', 75, TRUE)
|
||||
var/turf/targeted_turf = get_turf(target)
|
||||
var/turf/user_turf = get_turf(user)
|
||||
|
||||
new /obj/effect/temp_visual/voidswap(user.drop_location())
|
||||
new /obj/effect/temp_visual/voidswap(targeted_turf)
|
||||
|
||||
if(isliving(target) || iscontainer(target))
|
||||
do_teleport(user,targeted_turf,0,TRUE,no_effects = TRUE,channel=TELEPORT_CHANNEL_MAGIC)
|
||||
do_teleport(target,user_turf,0,TRUE,no_effects = TRUE,channel=TELEPORT_CHANNEL_MAGIC)
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/boogie_woogie/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!isliving(target) && !iscontainer(target))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You are unable to swap with the [target]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch //placeholder spell
|
||||
name = "Void's Push"
|
||||
desc = "With the snap of your fingers, send your enemies away."
|
||||
charge_max = 400
|
||||
clothes_req = FALSE
|
||||
invocation = "ISN'YKTI"
|
||||
invocation_type = "shout"
|
||||
range = 3
|
||||
selection_type = "view"
|
||||
sound = 'sound/magic/voidblink.ogg'
|
||||
action_background_icon_state = "bg_ecult"
|
||||
sparkle_path = /obj/effect/temp_visual/voidpush
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch/cast(list/targets,mob/user = usr)
|
||||
user.emote("snap")
|
||||
..(targets, user, 60)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/domain_expansion
|
||||
name = "Infinite Void"
|
||||
desc = "Create a domain that will slow down and mark all opponents with a void mark."
|
||||
charge_max = 1200
|
||||
clothes_req = FALSE
|
||||
invocation = "RYO'IKI TEN'KAI"
|
||||
invocation_type = "none"
|
||||
range = 0
|
||||
action_icon_state = "time"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
var/timestop_range = 7
|
||||
var/timestop_duration = 200
|
||||
var/static/mutable_appearance/halo
|
||||
var/sound/Snd // shamelessly ripped from lightning.
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/domain_expansion/cast(list/targets, mob/user = usr)
|
||||
Snd = new/sound('sound/magic/clockwork/ratvar_attack.ogg',channel = 7)
|
||||
halo = halo || mutable_appearance('icons/effects/effects.dmi', "at_shield2", EFFECTS_LAYER)
|
||||
user.add_overlay(halo)
|
||||
playsound(get_turf(user), Snd, 50, 0)
|
||||
if(do_mob(user,user,50,1))
|
||||
user.cut_overlay(halo)
|
||||
user.emote("clap1")
|
||||
user.say("DOM'ENO ISPLETIMAS")
|
||||
playsound(user, 'sound/magic/domain.ogg', 125, TRUE)
|
||||
new /obj/effect/domain_expansion(get_turf(user), timestop_range, timestop_duration, list(user))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
job_rank = ROLE_HERETIC
|
||||
antag_hud_type = ANTAG_HUD_HERETIC
|
||||
antag_hud_name = "heretic_beast"
|
||||
show_in_antagpanel = FALSE
|
||||
var/datum/antagonist/master
|
||||
|
||||
/datum/antagonist/heretic_monster/admin_add(datum/mind/new_owner,mob/admin)
|
||||
@@ -29,6 +30,7 @@
|
||||
var/datum/objective/master_obj = new
|
||||
master_obj.owner = src
|
||||
master_obj.explanation_text = "Assist your master in any way you can!"
|
||||
master_obj.completed = TRUE
|
||||
objectives += master_obj
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, "<span class='boldannounce'>Your master is [master.owner.current.real_name]</span>")
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/obj/structure/eldritch_crucible
|
||||
name = "Mawed Crucible"
|
||||
desc = "Immortalized cast iron, the steel-like teeth holding it in place, it's vile extract has the power of rebirthing things, remaking them from the very beginning."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "crucible"
|
||||
anchored = FALSE
|
||||
density = TRUE
|
||||
///How much mass this currently holds
|
||||
var/current_mass = 5
|
||||
///Maximum amount of mass
|
||||
var/max_mass = 5
|
||||
///Check to see if it is currently being used.
|
||||
var/in_use = FALSE
|
||||
|
||||
/obj/structure/eldritch_crucible/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_HERETIC(user) && !IS_HERETIC_MONSTER(user))
|
||||
return
|
||||
if(current_mass < max_mass)
|
||||
. += "The Crucible requires [max_mass - current_mass] more organs or bodyparts!"
|
||||
else
|
||||
. += "The Crucible is ready to be used!"
|
||||
|
||||
. += "You can anchor and reanchor it using Codex Cicatrix!"
|
||||
. += "It is currently [anchored == FALSE ? "unanchored" : "anchored"]"
|
||||
. += "This structure can brew 'Brew of Crucible soul' - when used it gives you the ability to phase through matter for 15 seconds, after the time elapses it teleports you back to your original location"
|
||||
. += "This structure can brew 'Brew of Dusk and Dawn' - when used it gives you xray for 1 minute"
|
||||
. += "This structure can brew 'Brew of Wounded Soldier' - when used it makes you immune to damage slowdown, additionally you start healing for every wound you have, quickly outpacing the damage caused by them."
|
||||
|
||||
/obj/structure/eldritch_crucible/attacked_by(obj/item/I, mob/living/user)
|
||||
if(istype(I,/obj/item/nullrod))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(!IS_HERETIC(user) && !IS_HERETIC_MONSTER(user))
|
||||
if(iscarbon(user))
|
||||
devour(user)
|
||||
return
|
||||
|
||||
if(istype(I,/obj/item/forbidden_book))
|
||||
playsound(src, 'sound/misc/desceration-02.ogg', 75, TRUE)
|
||||
anchored = !anchored
|
||||
to_chat(user,"<span class='notice'>You [anchored == FALSE ? "unanchor" : "anchor"] the crucible</span>")
|
||||
return
|
||||
|
||||
if(istype(I,/obj/item/bodypart) || istype(I,/obj/item/organ))
|
||||
//Both organs and bodyparts hold information if they are organic or robotic in the exact same way.
|
||||
var/obj/item/bodypart/forced = I
|
||||
if(forced.status != BODYPART_ORGANIC)
|
||||
return
|
||||
|
||||
if(current_mass >= max_mass)
|
||||
to_chat(user,"<span class='notice'> Crucible is already full!</span>")
|
||||
return
|
||||
playsound(src, 'sound/items/eatfood.ogg', 100, TRUE)
|
||||
to_chat(user,"<span class='notice'>Crucible devours [I.name] and fills itself with a little bit of liquid!</span>")
|
||||
current_mass++
|
||||
qdel(I)
|
||||
update_icon_state()
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/structure/eldritch_crucible/attack_hand(mob/user)
|
||||
if(!IS_HERETIC(user) && !IS_HERETIC_MONSTER(user))
|
||||
if(iscarbon(user))
|
||||
devour(user)
|
||||
return
|
||||
|
||||
if(in_use)
|
||||
to_chat(user,"<span class='notice'>Crucible is already in use!</span>")
|
||||
return
|
||||
|
||||
if(current_mass < max_mass)
|
||||
to_chat(user,"<span class='notice'>Crucible isn't full! Bring it more organs or bodyparts!</span>")
|
||||
return
|
||||
|
||||
in_use = TRUE
|
||||
var/list/lst = list()
|
||||
for(var/X in subtypesof(/obj/item/eldritch_potion))
|
||||
var/obj/item/eldritch_potion/potion = X
|
||||
lst[initial(potion.name)] = potion
|
||||
var/type = lst[input(user,"Choose your brew","Brew") in lst]
|
||||
playsound(src, 'sound/misc/desceration-02.ogg', 75, TRUE)
|
||||
new type(drop_location())
|
||||
current_mass = 0
|
||||
in_use = FALSE
|
||||
update_icon_state()
|
||||
|
||||
///Proc that eats the active limb of the victim
|
||||
/obj/structure/eldritch_crucible/proc/devour(mob/living/carbon/user)
|
||||
if(HAS_TRAIT(user,TRAIT_NODISMEMBER))
|
||||
return
|
||||
playsound(src, 'sound/items/eatfood.ogg', 100, TRUE)
|
||||
to_chat(user,"<span class='danger'>Crucible grabs your arm and devours it whole!</span>")
|
||||
var/obj/item/bodypart/arm = user.get_active_hand()
|
||||
arm.dismember()
|
||||
qdel(arm)
|
||||
current_mass += current_mass < max_mass ? 1 : 0
|
||||
update_icon_state()
|
||||
|
||||
/obj/structure/eldritch_crucible/update_icon_state()
|
||||
. = ..()
|
||||
if(current_mass == max_mass)
|
||||
icon_state = "crucible"
|
||||
else
|
||||
icon_state = "crucible_empty"
|
||||
|
||||
/obj/structure/trap/eldritch
|
||||
name = "elder carving"
|
||||
desc = "Collection of unknown symbols, they remind you of days long gone..."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
charges = 1
|
||||
///Owner of the trap
|
||||
var/mob/owner
|
||||
|
||||
/obj/structure/trap/eldritch/Crossed(atom/movable/AM)
|
||||
if(!isliving(AM))
|
||||
return ..()
|
||||
var/mob/living/living_mob = AM
|
||||
if((owner && living_mob == owner) || IS_HERETIC(living_mob) || IS_HERETIC_MONSTER(living_mob))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/trap/eldritch/attacked_by(obj/item/I, mob/living/user)
|
||||
. = ..()
|
||||
if(istype(I,/obj/item/melee/rune_knife) || istype(I,/obj/item/nullrod))
|
||||
qdel(src)
|
||||
|
||||
///Proc that sets the owner
|
||||
/obj/structure/trap/eldritch/proc/set_owner(mob/_owner)
|
||||
owner = _owner
|
||||
|
||||
/obj/structure/trap/eldritch/alert
|
||||
name = "alert carving"
|
||||
icon_state = "alert_rune"
|
||||
alpha = 10
|
||||
|
||||
/obj/structure/trap/eldritch/alert/trap_effect(mob/living/L)
|
||||
if(owner)
|
||||
to_chat(owner,"<span class='big boldwarning'>[L.real_name] has stepped foot on the alert rune in [get_area(src)]!</span>")
|
||||
return ..()
|
||||
|
||||
//this trap can only get destroyed by rune carving knife or nullrod
|
||||
/obj/structure/trap/eldritch/alert/flare()
|
||||
return
|
||||
|
||||
/obj/structure/trap/eldritch/tentacle
|
||||
name = "grasping carving"
|
||||
icon_state = "tentacle_rune"
|
||||
|
||||
/obj/structure/trap/eldritch/tentacle/trap_effect(mob/living/L)
|
||||
if(!iscarbon(L))
|
||||
return
|
||||
var/mob/living/carbon/carbon_victim = L
|
||||
carbon_victim.DefaultCombatKnockdown(50)
|
||||
carbon_victim.drop_all_held_items()
|
||||
carbon_victim.apply_damage(20,BRUTE,BODY_ZONE_R_LEG)
|
||||
carbon_victim.apply_damage(20,BRUTE,BODY_ZONE_L_LEG)
|
||||
playsound(src, 'sound/magic/demon_attack1.ogg', 75, TRUE)
|
||||
return ..()
|
||||
|
||||
/obj/structure/trap/eldritch/mad
|
||||
name = "mad carving"
|
||||
icon_state = "madness_rune"
|
||||
|
||||
/obj/structure/trap/eldritch/mad/trap_effect(mob/living/L)
|
||||
if(!iscarbon(L))
|
||||
return
|
||||
var/mob/living/carbon/carbon_victim = L
|
||||
carbon_victim.adjustStaminaLoss(60)
|
||||
carbon_victim.silent += 10
|
||||
carbon_victim.confused += 5
|
||||
carbon_victim.Jitter(10)
|
||||
carbon_victim.Dizzy(20)
|
||||
carbon_victim.blind_eyes(2)
|
||||
SEND_SIGNAL(carbon_victim, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
return ..()
|
||||
@@ -1,18 +1,18 @@
|
||||
/datum/eldritch_knowledge/base_ash
|
||||
name = "Nightwatcher's Secret"
|
||||
desc = "Inducts you into the Path of Ash. Allows you to transmute a match with an eldritch blade into an ashen blade."
|
||||
gain_text = "The City guard knows their watch. If you ask them at night they may tell you about the ashy lantern."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/flesh_final)
|
||||
desc = "Inducts you into the Path of Ash. Allows you to transmute a match with a spear into an ashen blade."
|
||||
gain_text = "The City Guard know their watch. If you ask them at night, they may tell you about the ashy lantern."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/flesh_final,/datum/eldritch_knowledge/final/void_final,/datum/eldritch_knowledge/base_void)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/ashen_grasp)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/match)
|
||||
required_atoms = list(/obj/item/spear,/obj/item/match)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/ash)
|
||||
cost = 1
|
||||
cost = 0
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/spell/ashen_shift
|
||||
name = "Ashen Shift"
|
||||
gain_text = "Ash is all the same, how can one man master it all?"
|
||||
desc = "A short range jaunt that will enable you to escape from danger."
|
||||
gain_text = "The Nightwatcher was the first of them, his treason started it all."
|
||||
desc = "A short range jaunt that can help you escape from bad situations."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
|
||||
next_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/essence,/datum/eldritch_knowledge/ashen_eyes)
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/datum/eldritch_knowledge/ashen_grasp
|
||||
name = "Grasp of Ash"
|
||||
gain_text = "Gates have opened, minds have flooded, yet I remain."
|
||||
gain_text = "He knew how to walk between the planes."
|
||||
desc = "Empowers your mansus grasp to knock enemies down and throw them away."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift)
|
||||
@@ -32,7 +32,7 @@
|
||||
return
|
||||
|
||||
var/mob/living/carbon/C = target
|
||||
var/datum/status_effect/eldritch/E = C.has_status_effect(/datum/status_effect/eldritch/rust) || C.has_status_effect(/datum/status_effect/eldritch/ash) || C.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
var/datum/status_effect/eldritch/E = C.has_status_effect(/datum/status_effect/eldritch/rust) || C.has_status_effect(/datum/status_effect/eldritch/ash) || C.has_status_effect(/datum/status_effect/eldritch/flesh) || C.has_status_effect(/datum/status_effect/eldritch/void)
|
||||
if(E)
|
||||
. = TRUE
|
||||
E.on_effect()
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
/datum/eldritch_knowledge/ashen_eyes
|
||||
name = "Ashen Eyes"
|
||||
gain_text = "Piercing eyes may guide me through the mundane."
|
||||
gain_text = "Piercing eyes, guide me through the mundane."
|
||||
desc = "Allows you to craft thermal vision amulet by transmutating eyes with a glass shard."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift,/datum/eldritch_knowledge/flesh_ghoul)
|
||||
@@ -58,11 +58,11 @@
|
||||
|
||||
/datum/eldritch_knowledge/ash_mark
|
||||
name = "Mark of Ash"
|
||||
gain_text = "Spread the famine."
|
||||
desc = "Your sickly blade now applies ash mark on hit. Use your mansus grasp to proc the mark. Mark of Ash causes stamina damage, and fire loss, and spreads to a nearby carbon. Damage decreases with how many times the mark has spread."
|
||||
gain_text = "The Nightwatcher was a very particular man, always watching in the dead of night. But in spite of his duty, he regularly tranced through the manse with his blazing lantern held high."
|
||||
desc = "Your Mansus Grasp now applies the Mark of Ash on hit. Attack the afflicted with your Sickly Blade to detonate the mark. Upon detonation, the Mark of Ash causes stamina damage and burn damage, and spreads to an additional nearby opponent. The damage decreases with each spread."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/flesh_mark)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/mad_mask)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/void_mark)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
@@ -71,28 +71,20 @@
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/ash,5)
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness
|
||||
name = "Curse of Blindness"
|
||||
gain_text = "The blind man walks through the world, unnoticed by the masses."
|
||||
desc = "Curse someone with 2 minutes of complete blindness by sacrificing a pair of eyes, a screwdriver and a pool of blood, with an object that the victim has touched with their bare hands."
|
||||
/datum/eldritch_knowledge/mad_mask
|
||||
name = "Mask of Madness"
|
||||
gain_text = "He walks the world, unnoticed by the masses."
|
||||
desc = "Allows you to transmute any mask, with a candle and a pair of eyes, to create a mask of madness, It causes passive stamina damage to everyone around the wearer and hallucinations, can be forced on a non believer to make him unable to take it off..."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/screwdriver,/obj/effect/decal/cleanable/blood)
|
||||
result_atoms = list(/obj/item/clothing/mask/void_mask)
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/clothing/mask,/obj/item/candle)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/curse/paralysis)
|
||||
timer = 2 MINUTES
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness/curse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.become_blind(MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness/uncurse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.cure_blind(MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/spell/flame_birth
|
||||
name = "Fiery Rebirth"
|
||||
gain_text = "Nightwatcher was a man of principles, and yet he arose from the chaos he vowed to protect from."
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
|
||||
gain_text = "The Nightwatcher was a man of principles, and yet his power arose from the chaos he vowed to combat."
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a target is in critical condition it drains the last of their vitality, killing them."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/flame_immunity)
|
||||
@@ -122,12 +114,12 @@
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_blade_upgrade
|
||||
name = "Blazing Steel"
|
||||
gain_text = "May the sun burn the heretics."
|
||||
desc = "Your blade of choice will now add firestacks."
|
||||
name = "Fiery Blade"
|
||||
gain_text = "Blade in hand, he swung and swung as the ash fell from the skies. His city, his people... all burnt to cinders, and yet life still remained in his charred body."
|
||||
desc = "Your blade of choice will now light your enemies ablaze."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/flame_birth)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/void_blade_upgrade)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
@@ -140,10 +132,10 @@
|
||||
/datum/eldritch_knowledge/curse/corrosion
|
||||
name = "Curse of Corrosion"
|
||||
gain_text = "Cursed land, cursed man, cursed mind."
|
||||
desc = "Curse someone for 2 minutes of vomiting and major organ damage. Using a wirecutter, a spill of blood, a heart, left arm and a right arm, and an item that the victim touched with their bare hands."
|
||||
desc = "Curse someone for 2 minutes of vomiting and major organ damage. Using a wirecutter, a pool of blood, a heart, left arm and a right arm, and an item that the victim touched with their bare hands."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/wirecutters,/obj/effect/decal/cleanable/blood,/obj/item/organ/heart,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/mad_mask,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
timer = 2 MINUTES
|
||||
|
||||
/datum/eldritch_knowledge/curse/corrosion/curse(mob/living/chosen_mob)
|
||||
@@ -157,10 +149,10 @@
|
||||
/datum/eldritch_knowledge/curse/paralysis
|
||||
name = "Curse of Paralysis"
|
||||
gain_text = "Corrupt their flesh, make them bleed."
|
||||
desc = "Curse someone for 5 minutes of inability to walk. Using a knife, pool of blood, left leg, right leg, a hatchet and an item that the victim touched with their bare hands. "
|
||||
desc = "Curse someone for 5 minutes of inability to walk. Sacrifice a knife, a pool of blood, a pair of legs, a hatchet and an item that the victim touched with their bare hands. "
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/effect/decal/cleanable/blood,/obj/item/bodypart/l_leg,/obj/item/bodypart/r_leg,/obj/item/hatchet)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/mad_mask,/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
timer = 5 MINUTES
|
||||
|
||||
/datum/eldritch_knowledge/curse/paralysis/curse(mob/living/chosen_mob)
|
||||
@@ -177,7 +169,7 @@
|
||||
|
||||
/datum/eldritch_knowledge/spell/cleave
|
||||
name = "Blood Cleave"
|
||||
gain_text = "At first I was unfamiliar with these instruments of war, but the priest told me how to use them."
|
||||
gain_text = "At first I didn't understand these instruments of war, but the priest told me to use them regardless. Soon, he said, I would know them well."
|
||||
desc = "Grants a spell that will inflict wounds and bleeding upon the target, as well as in a short radius around them."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/cleave
|
||||
@@ -185,15 +177,15 @@
|
||||
|
||||
/datum/eldritch_knowledge/final/ash_final
|
||||
name = "Ashlord's Rite"
|
||||
gain_text = "The forgotten lords have spoken! The Lord of Ash has come! Fear the flame!"
|
||||
desc = "Bring three corpses onto a transmutation rune, after ascending you will become immune to fire, space, temperature and other environmental hazards. You will develop resistance to all other damages. You will be granted two spells, one which can bring forth a cascade of massive fire, and another which will surround your body in precious flames for a minute."
|
||||
gain_text = "The Nightwatcher found the rite and shared it amongst mankind! For now I am one with the fire, WITNESS MY ASCENSION!"
|
||||
desc = "Bring 3 corpses onto a transmutation rune, you will become immune to fire, the vacuum of space, cold and other enviromental hazards and become overall sturdier to all other damages. You will gain a spell that passively creates ring of fire around you as well ,as you will gain a powerful ability that lets you create a wave of flames all around you."
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
cost = 5
|
||||
route = PATH_ASH
|
||||
var/list/trait_list = list(TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE)
|
||||
|
||||
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for the Ashlord, [user.real_name] has ascended! The flames shall consume all! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big)
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/fire_sworn)
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -201,6 +193,7 @@
|
||||
H.physiology.burn_mod *= 0.5
|
||||
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
H.client?.give_award(/datum/award/achievement/misc/ash_ascension, H)
|
||||
for(var/X in trait_list)
|
||||
ADD_TRAIT(user,X,MAGIC_TRAIT)
|
||||
return ..()
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/datum/eldritch_knowledge/base_flesh
|
||||
name = "Principle of Hunger"
|
||||
desc = "Inducts you into the Path of Flesh. Allows you to transmute a pool of blood with your eldritch blade into a Blade of Flesh."
|
||||
gain_text = "Hundred's of us starved, but I.. I found the strength in my greed."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/rust_final)
|
||||
desc = "Inducts you into the Path of Flesh. Allows you to transmute a pool of blood with a spear into a Blade of Flesh."
|
||||
gain_text = "Hundreds of us starved, but not me... I found strength in my greed."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/void_final,/datum/eldritch_knowledge/base_void)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_grasp)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/effect/decal/cleanable/blood)
|
||||
required_atoms = list(/obj/item/spear,/obj/effect/decal/cleanable/blood)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/flesh)
|
||||
cost = 1
|
||||
cost = 0
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_ghoul
|
||||
name = "Imperfect Ritual"
|
||||
desc = "Allows you to resurrect the dead as voiceless dead by sacrificing them on the transmutation rune with a poppy. Voiceless dead are mute and have 50 HP. You can only have 2 at a time."
|
||||
gain_text = "I found notes... notes of a ritual, scraps, unfinished, and yet... I still did it."
|
||||
gain_text = "I found notes of a dark ritual, unfinished... yet still, I pushed forward."
|
||||
cost = 1
|
||||
required_atoms = list(/mob/living/carbon/human,/obj/item/reagent_containers/food/snacks/grown/poppy)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/ashen_eyes)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/void_cloak,/datum/eldritch_knowledge/ashen_eyes)
|
||||
route = PATH_FLESH
|
||||
var/max_amt = 2
|
||||
var/current_amt = 0
|
||||
@@ -66,8 +66,8 @@
|
||||
|
||||
/datum/eldritch_knowledge/flesh_grasp
|
||||
name = "Grasp of Flesh"
|
||||
gain_text = "'My newfound desire, it drove me to do great things,' The Priest said."
|
||||
desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks."
|
||||
gain_text = "My new found desires drove me to greater and greater heights."
|
||||
desc = "Empowers your mansus grasp to be able to create a single ghoul out of a dead person. Ghouls are only half as sturdy as a regular person and look like husks to the heathens' eyes."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul)
|
||||
var/ghoul_amt = 4
|
||||
@@ -78,8 +78,12 @@
|
||||
. = ..()
|
||||
if(!ishuman(target) || target == user)
|
||||
return
|
||||
|
||||
if(iscarbon(target))
|
||||
user.reagents.add_reagent(/datum/reagent/eldritch, 5)
|
||||
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
var/datum/status_effect/eldritch/eldritch_effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
var/datum/status_effect/eldritch/eldritch_effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh) || human_target.has_status_effect(/datum/status_effect/eldritch/void)
|
||||
if(eldritch_effect)
|
||||
. = TRUE
|
||||
eldritch_effect.on_effect()
|
||||
@@ -131,25 +135,26 @@
|
||||
/datum/eldritch_knowledge/flesh_mark
|
||||
name = "Mark of Flesh"
|
||||
gain_text = "I saw them, the marked ones. The screams... the silence."
|
||||
desc = "Your sickly blade now applies a mark of flesh to those cut by it. Once marked, using your Mansus Grasp upon them will cause additional bleeding from the target."
|
||||
desc = "Your Mansus Grasp now applies the Mark of Flesh on hit. Attack the afflicted with your Sickly Blade to detonate the mark. Upon detonation, the Mark of Flesh causes additional bleeding."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/void_mark)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
/datum/eldritch_knowledge/flesh_mark/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
. = TRUE
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade
|
||||
name = "Bleeding Steel"
|
||||
gain_text = "It rained blood, that's when I understood the gravekeeper's advice."
|
||||
desc = "Your blade will now cause additional bleeding to those hit by it."
|
||||
gain_text = "And then, blood rained from the heavens. That's when I finally understood the Marshal's teachings."
|
||||
desc = "Your Sickly Blade will now cause additional bleeding."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/void_blade_upgrade)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
@@ -162,18 +167,18 @@
|
||||
|
||||
/datum/eldritch_knowledge/summon/raw_prophet
|
||||
name = "Raw Ritual"
|
||||
gain_text = "The uncanny man walks alone in the valley, I was able to call his aid."
|
||||
desc = "You can now summon a Raw Prophet using eyes, a left arm, right arm and a pool of blood using a transmutation circle. Raw prophets have increased seeing range, and can see through walls. They can jaunt long distances, though they are fragile."
|
||||
gain_text = "The Uncanny Man, who walks alone in the valley between the worlds... I was able to summon his aid."
|
||||
desc = "You can now summon a Raw Prophet by transmutating a pair of eyes, a left arm and a pool of blood. Raw prophets have increased seeing range, as well as X-Ray vision, but they are very fragile."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm,/obj/effect/decal/cleanable/blood)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/raw_prophet
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/curse/paralysis)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/rune_carver,/datum/eldritch_knowledge/curse/paralysis)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/summon/stalker
|
||||
name = "Lonely Ritual"
|
||||
gain_text = "I was able to combine my greed and desires to summon an eldritch beast I have not seen before."
|
||||
desc = "You can now summon a Stalker using a knife, a candle, a pen and a piece of paper using a transmutation circle. Stalkers possess the ability to shapeshift into various forms while assuming the vigor and powers of that form."
|
||||
gain_text = "I was able to combine my greed and desires to summon an eldritch beast I had never seen before. An ever shapeshifting mass of flesh, it knew well my goals."
|
||||
desc = "You can now summon a Stalker by transmutating a kitchen knife, a candle, a pen and a piece of paper. Stalkers can shapeshift into harmless animals to get close to the victim."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/item/candle,/obj/item/pen,/obj/item/paper)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker
|
||||
@@ -182,7 +187,7 @@
|
||||
|
||||
/datum/eldritch_knowledge/summon/ashy
|
||||
name = "Ashen Ritual"
|
||||
gain_text = "I combined principle of hunger with desire of destruction. The eyeful lords have noticed me."
|
||||
gain_text = "I combined my principle of hunger with my desire for destruction. And the Nightwatcher knew my name."
|
||||
desc = "You can now summon an Ashen One by transmuting a pile of ash, a head and a book using a transmutation circle. They possess the ability to jaunt short distances and create a cascade of flames."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/effect/decal/cleanable/ash,/obj/item/bodypart/head,/obj/item/book)
|
||||
@@ -191,63 +196,43 @@
|
||||
|
||||
/datum/eldritch_knowledge/summon/rusty
|
||||
name = "Rusted Ritual"
|
||||
gain_text = "I combined principle of hunger with desire of corruption. The rusted hills call my name."
|
||||
desc = "You can now summon a Rust Walker transmuting a vomit pool, a head, and a book using a transmutation circle. Rust Walkers possess the ability to spread rust and can fire bolts of rust to further corrode the area."
|
||||
gain_text = "I combined my principle of hunger with my desire for corruption. And the Rusted Hills called my name."
|
||||
desc = "You can now summon a Rust Walker by transmuting a vomit pool, a severed head, and a book using a transmutation circle. Rust Walkers possess the ability to spread rust and can fire bolts of rust to further corrode the area."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/effect/decal/cleanable/vomit,/obj/item/bodypart/head,/obj/item/book)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/rust_spirit
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/entropic_plume)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/voidpull,/datum/eldritch_knowledge/spell/entropic_plume)
|
||||
|
||||
/datum/eldritch_knowledge/spell/blood_siphon
|
||||
name = "Blood Siphon"
|
||||
gain_text = "Our blood is all the same after all, the owl told me."
|
||||
desc = "You are granted a spell that drains some of the targets health, and returns it to you. It also has a chance to transfer any wounds you possess onto the target."
|
||||
gain_text = "No matter the man, we bleed all the same. That's what the Marshal told me."
|
||||
desc = "You gain a spell that drains lifeforce from your enemies to restore your own."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/blood_siphon
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/voidpull)
|
||||
|
||||
/datum/eldritch_knowledge/final/flesh_final
|
||||
name = "Priest's Final Hymn"
|
||||
gain_text = "Man of this world. Hear me! For the time of the lord of arms has come!"
|
||||
desc = "Bring three corpses to a transmutation rune to either ascend as The Lord of the Night or summon a single Terror of the Night, however you cannot ascend more than once."
|
||||
gain_text = "Men of this world. Hear me, for the time of the Lord of Arms has come! The Emperor of Flesh guides my army!"
|
||||
desc = "Bring 3 bodies onto a transmutation rune to shed your human form and ascend to untold power."
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
cost = 5
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/final/flesh_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
var/alert_ = alert(user,"Do you want to ascend as the lord of the night or just summon a terror of the night?","...","Yes","No")
|
||||
user.SetImmobilized(10 HOURS) // no way someone will stand 10 hours in a spot, just so he can move while the alert is still showing.
|
||||
switch(alert_)
|
||||
if("No")
|
||||
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy(loc)
|
||||
message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]")
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]", ROLE_HERETIC, null, ROLE_HERETIC, 100,summoned)
|
||||
user.SetImmobilized(0)
|
||||
if(LAZYLEN(candidates) == 0)
|
||||
to_chat(user,"<span class='warning'>No ghost could be found...</span>")
|
||||
qdel(summoned)
|
||||
return FALSE
|
||||
var/mob/dead/observer/ghost_candidate = pick(candidates)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for vassal of arms has ascended! Terror of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
log_game("[key_name_admin(ghost_candidate)] has taken control of ([key_name_admin(summoned)]).")
|
||||
summoned.ghostize(FALSE)
|
||||
summoned.key = ghost_candidate.key
|
||||
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic_monster/monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
monster.set_owner(master)
|
||||
master.ascended = TRUE
|
||||
if("Yes")
|
||||
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy/prime(loc,TRUE,10)
|
||||
summoned.ghostize(0)
|
||||
user.SetImmobilized(0)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for king of arms has ascended! Lord of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
log_game("[user.real_name] ascended as [summoned.real_name]")
|
||||
var/mob/living/carbon/carbon_user = user
|
||||
var/datum/antagonist/heretic/ascension = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
carbon_user.mind.transfer_to(summoned, TRUE)
|
||||
carbon_user.gib()
|
||||
. = ..()
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Ever coiling vortex. Reality unfolded. THE LORD OF ARMS, [user.real_name] has ascended! Fear the ever twisting hand! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shed_human_form)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.physiology.brute_mod *= 0.5
|
||||
H.physiology.burn_mod *= 0.5
|
||||
var/datum/antagonist/heretic/heretic = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
var/datum/eldritch_knowledge/flesh_grasp/ghoul1 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_grasp)
|
||||
ghoul1.ghoul_amt *= 3
|
||||
var/datum/eldritch_knowledge/flesh_ghoul/ghoul2 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_ghoul)
|
||||
ghoul2.max_amt *= 3
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/datum/eldritch_knowledge/base_rust
|
||||
name = "Blacksmith's Tale"
|
||||
desc = "Inducts you into the Path of Rust. Allows you to transmute an eldritch blade with any trash item into a Blade of Rust."
|
||||
gain_text = "'Let me tell you a story,' The Blacksmith said as he gazed into his rusty blade."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final)
|
||||
desc = "Inducts you into the Path of Rust. Allows you to transmute a spear with any trash item into a Blade of Rust."
|
||||
gain_text = "'Let me tell you a story', said the Blacksmith, as he gazed deep into his rusty blade."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final,/datum/eldritch_knowledge/final/void_final,/datum/eldritch_knowledge/base_void)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_fist)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/trash)
|
||||
required_atoms = list(/obj/item/spear,/obj/item/trash)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/rust)
|
||||
cost = 1
|
||||
cost = 0
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist
|
||||
name = "Grasp of Rust"
|
||||
desc = "Empowers your Mansus Grasp to deal 500 damage to non-living matter and rust any structure it touches. Destroys already rusted structures."
|
||||
gain_text = "Rust grows on the ceiling of the mansus."
|
||||
desc = "Empowers your Mansus Grasp to deal 500 damage to non-living matter and rust any surface it touches. Already rusted surfaces are destroyed."
|
||||
gain_text = "On the ceiling of the Mansus, rust grows as moss does on a stone."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen)
|
||||
var/rust_force = 500
|
||||
@@ -20,35 +20,40 @@
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
var/check = FALSE
|
||||
if(ismob(target))
|
||||
var/mob/living/mobster = target
|
||||
if(!mobster.mob_biotypes & MOB_ROBOTIC)
|
||||
return FALSE
|
||||
else
|
||||
check = TRUE
|
||||
if(user.a_intent == INTENT_HARM || check)
|
||||
target.rust_heretic_act()
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist/on_eldritch_blade(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh) || H.has_status_effect(/datum/status_effect/eldritch/void)
|
||||
if(E)
|
||||
E.on_effect()
|
||||
H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25)
|
||||
else
|
||||
for(var/X in user.mind.spell_list)
|
||||
if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp))
|
||||
continue
|
||||
var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X
|
||||
MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max)
|
||||
target.rust_heretic_act()
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/spell/area_conversion
|
||||
name = "Aggressive Spread"
|
||||
desc = "Spreads rust to nearby turfs. Destroys already rusted walls."
|
||||
gain_text = "All wise men know not to touch the bound king."
|
||||
desc = "Spreads rust to nearby surfaces. Already rusted surfaces are destroyed."
|
||||
gain_text = "All wise men know well not to touch the Bound King."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/rust_conversion
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/spell/rust_wave)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/crucible,/datum/eldritch_knowledge/spell/rust_wave)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/spell/rust_wave
|
||||
name = "Patron's Reach"
|
||||
desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit."
|
||||
gain_text = "Messengers of hope fear the Rustbringer."
|
||||
gain_text = "Messengers of Hope, fear the Rustbringer!"
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave
|
||||
route = PATH_RUST
|
||||
@@ -56,7 +61,7 @@
|
||||
/datum/eldritch_knowledge/rust_regen
|
||||
name = "Leeching Walk"
|
||||
desc = "Passively heals you when you are on rusted tiles."
|
||||
gain_text = "'The strength was unparalleled, unnatural.' The Blacksmith was smiling."
|
||||
gain_text = "The strength was unparalleled, unnatural. The Blacksmith was smiling."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/essence)
|
||||
route = PATH_RUST
|
||||
@@ -75,26 +80,27 @@
|
||||
|
||||
/datum/eldritch_knowledge/rust_mark
|
||||
name = "Mark of Rust"
|
||||
desc = "Your eldritch blade now applies a rust mark. Rust marks have a chance to deal between 0 to 200 damage to 75% of enemies items. To activate the mark use your Mansus Grasp on it."
|
||||
gain_text = "Lords of the depths help those in dire need at a cost."
|
||||
desc = "Your Mansus Grasp now applies the Mark of Rust on hit. Attack the afflicted with your Sickly Blade to detonate the mark. Upon detonation, the Mark of Rust has a chance to deal between 0 to 200 damage to 75% of your enemy's held items."
|
||||
gain_text = "Rusted Hills help those in dire need at a cost."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/area_conversion)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/void_mark)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
/datum/eldritch_knowledge/rust_mark/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
. = TRUE
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/rust)
|
||||
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade
|
||||
name = "Toxic Steel"
|
||||
gain_text = "Let the blade guide you through the flesh."
|
||||
desc = "Your blade of choice will now add toxin to enemies bloodstream."
|
||||
name = "Toxic Blade"
|
||||
gain_text = "The Blade will guide you through the flesh, should you let it."
|
||||
desc = "Your blade of choice will now poison your enemies on hit."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/void_blade_upgrade)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(mob/target,user,proximity_flag,click_parameters)
|
||||
@@ -106,8 +112,8 @@
|
||||
|
||||
/datum/eldritch_knowledge/spell/entropic_plume
|
||||
name = "Entropic Plume"
|
||||
desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust."
|
||||
gain_text = "If they knew, the truth would turn them against eachother."
|
||||
desc = "You can now send a disorienting plume of pure entropy that blinds, poisons and makes enemies strike each other. It also rusts any tiles it affects."
|
||||
gain_text = "The slightest glimmer of truth would turn them against eachother."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_fist_upgrade,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
|
||||
@@ -115,26 +121,26 @@
|
||||
|
||||
/datum/eldritch_knowledge/armor
|
||||
name = "Armorer's Ritual"
|
||||
desc = "You can now create eldritch armor using a built table and a gas mask on top of a transmutation rune."
|
||||
gain_text = "For I am the heir to the throne of doom."
|
||||
desc = "You can now create Eldritch Armor using a table and a gas mask."
|
||||
gain_text = "The Rusted Hills welcomed the Blacksmith in their generosity."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/flesh_ghoul)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/cold_snap)
|
||||
required_atoms = list(/obj/structure/table,/obj/item/clothing/mask/gas)
|
||||
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch)
|
||||
|
||||
/datum/eldritch_knowledge/essence
|
||||
name = "Priest's Ritual"
|
||||
desc = "You can now transmute a tank of water into a bottle of eldritch fluid."
|
||||
gain_text = "This is an old recipe, I got it from an owl."
|
||||
desc = "You can now transmute a tank of water and a glass shard into a bottle of eldritch water."
|
||||
gain_text = "This is an old recipe. The Owl whispered it to me."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift)
|
||||
required_atoms = list(/obj/structure/reagent_dispensers/watertank)
|
||||
required_atoms = list(/obj/structure/reagent_dispensers/watertank,/obj/item/shard)
|
||||
result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch)
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist_upgrade
|
||||
name = "Vile Grip"
|
||||
desc = "Empowers your Mansus Grasp further, sickening your foes and making them vomit, while also strengthening the rate at which your hand decays objects."
|
||||
gain_text = "A sickly diseased touch that was, yet, so welcoming."
|
||||
gain_text = "His touch vile, terrible, and yet so terribly inviting.."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/grasp_of_decay)
|
||||
var/rust_force = 750
|
||||
@@ -145,13 +151,13 @@
|
||||
. = ..()
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
H.set_disgust(75)
|
||||
H.set_disgust(25)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/spell/grasp_of_decay
|
||||
name = "Grasp of Decay"
|
||||
desc = "Applying your knowledge of rust to the human body, a knowledge that could decay your foes from the inside out, resulting in organ failure, vomiting, or eventual death through peeling flesh."
|
||||
gain_text = "Decay, similar to Rust, yet so much more terribly uninviting."
|
||||
desc = "Applying your knowledge of rust to the human body, a knowledge that could decay your foes from the inside out, resulting in organ failure, vomiting, or eventual death through the peeling of rotting flesh."
|
||||
gain_text = "Rust, decay, it's all the same. All that remains is application."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/rust_final)
|
||||
@@ -160,7 +166,7 @@
|
||||
/datum/eldritch_knowledge/final/rust_final
|
||||
name = "Rustbringer's Oath"
|
||||
desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall."
|
||||
gain_text = "Champion of rust. Corruptor of steel. Fear the dark for Rustbringer has come!"
|
||||
gain_text = "Champion of rust. Corruptor of steel. Fear the dark for the Rustbringer has come! Rusted Hills, CALL MY NAME!"
|
||||
cost = 5
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
route = PATH_RUST
|
||||
@@ -169,7 +175,7 @@
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.physiology.brute_mod *= 0.5
|
||||
H.physiology.burn_mod *= 0.5
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for Rustbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for the Rustbringer, [user.real_name] has ascended! None shall escape the corrosion! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
new /datum/rust_spread(loc)
|
||||
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
@@ -183,7 +189,7 @@
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
human_user.adjustBruteLoss(-6, FALSE)
|
||||
human_user.adjustFireLoss(-6, FALSE)
|
||||
human_user.adjustToxLoss(-6, FALSE)
|
||||
human_user.adjustToxLoss(-6, FALSE, TRUE)
|
||||
human_user.adjustOxyLoss(-6, FALSE)
|
||||
human_user.adjustStaminaLoss(-20)
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/datum/eldritch_knowledge/base_void
|
||||
name = "Glimmer of Winter"
|
||||
desc = "Opens up the path of void to you. Allows you to transmute a spear in a sub-zero temperature into a void blade."
|
||||
gain_text = "I feel a shimmer in the air, atmosphere around me gets colder. I feel my body realizing the emptiness of existance. Something's watching me"
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/rust_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/void_grasp)
|
||||
required_atoms = list(/obj/item/spear)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/void)
|
||||
cost = 0
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/base_void/recipe_snowflake_check(list/atoms, loc)
|
||||
. = ..()
|
||||
var/turf/open/turfie = loc
|
||||
if(turfie.GetTemperature() > T0C)
|
||||
return FALSE
|
||||
|
||||
/datum/eldritch_knowledge/void_grasp
|
||||
name = "Grasp of Void"
|
||||
desc = "Temporarily mutes your victim, also lowers their body temperature."
|
||||
gain_text = "I found the cold watcher who observes me. The resonance of cold grows within me. This isn't the end of the mystery."
|
||||
cost = 1
|
||||
route = PATH_VOID
|
||||
next_knowledge = list(/datum/eldritch_knowledge/cold_snap)
|
||||
|
||||
/datum/eldritch_knowledge/void_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!iscarbon(target))
|
||||
return
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
var/turf/open/turfie = get_turf(carbon_target)
|
||||
turfie.TakeTemperature(-20)
|
||||
carbon_target.adjust_bodytemperature(-40)
|
||||
carbon_target.silent = clamp(carbon_target.silent + 4, 0, 20)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/void_grasp/on_eldritch_blade(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!ishuman(target))
|
||||
return
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh) || H.has_status_effect(/datum/status_effect/eldritch/void)
|
||||
if(!E)
|
||||
return
|
||||
E.on_effect()
|
||||
H.silent = clamp(H.silent + 3, 0, 20)
|
||||
|
||||
/datum/eldritch_knowledge/cold_snap
|
||||
name = "Aristocrat's Way"
|
||||
desc = "Makes you immune to cold temperatures, and you no longer need to breathe, you can still take damage from lack of pressure."
|
||||
gain_text = "I found a thread of cold breath. It lead me to a strange shrine, all made of crystals. Translucent and white, a depiction of a nobleman stood before me."
|
||||
cost = 1
|
||||
route = PATH_VOID
|
||||
next_knowledge = list(/datum/eldritch_knowledge/void_cloak,/datum/eldritch_knowledge/void_mark,/datum/eldritch_knowledge/armor)
|
||||
|
||||
/datum/eldritch_knowledge/cold_snap/on_gain(mob/user)
|
||||
. = ..()
|
||||
ADD_TRAIT(user,TRAIT_RESISTCOLD,MAGIC_TRAIT)
|
||||
ADD_TRAIT(user, TRAIT_NOBREATH, MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/cold_snap/on_lose(mob/user)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(user,TRAIT_RESISTCOLD,MAGIC_TRAIT)
|
||||
ADD_TRAIT(user, TRAIT_NOBREATH, MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/void_cloak
|
||||
name = "Void Cloak"
|
||||
desc = "A cloak that can become invisbile at will, hiding items you store in it. To create it transmute a glass shard, any item of clothing that you can fit over your uniform and any type of bedsheet."
|
||||
gain_text = "Owl is the keeper of things that quite not are in practice, but in theory are."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul,/datum/eldritch_knowledge/cold_snap)
|
||||
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/void)
|
||||
required_atoms = list(/obj/item/shard,/obj/item/clothing/suit,/obj/item/bedsheet)
|
||||
|
||||
/datum/eldritch_knowledge/void_mark
|
||||
name = "Mark of Void"
|
||||
gain_text = "A gust of wind? Maybe a shimmer in the air. Presence is overwhelming, my senses betrayed me, my mind is my enemy."
|
||||
desc = "Your mansus grasp now applies mark of void status effect. To proc the mark, use your sickly blade on the marked. Mark of void when procced lowers the victims body temperature significantly."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/void_phase)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/void_mark/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!isliving(target))
|
||||
return
|
||||
. = TRUE
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/void)
|
||||
|
||||
/datum/eldritch_knowledge/spell/void_phase
|
||||
name = "Void Phase"
|
||||
gain_text = "Reality bends under the power of memory, for all is fleeting, and what else stays?"
|
||||
desc = "You gain a long range pointed blink that allows you to instantly teleport to your location, it causes aoe damage around you and your chosen location."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/void_blink
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rune_carver,/datum/eldritch_knowledge/crucible,/datum/eldritch_knowledge/void_blade_upgrade)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/rune_carver
|
||||
name = "Carving Knife"
|
||||
gain_text = "Etched, carved... eternal. I can carve the monolith and evoke their powers!"
|
||||
desc = "You can create a carving knife, which allows you to create up to 3 carvings on the floor that have various effects on nonbelievers who walk over them. They make quite a handy throwing weapon. To create the carving knife transmute a knife with a glass shard and a piece of paper."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/void_phase,/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/item/shard,/obj/item/paper)
|
||||
result_atoms = list(/obj/item/melee/rune_knife)
|
||||
|
||||
/datum/eldritch_knowledge/crucible
|
||||
name = "Mawed Crucible"
|
||||
gain_text = "This is pure agony, i wasn't able to summon the dereliction of the emperor, but i stumbled upon a diffrent recipe..."
|
||||
desc = "Allows you to create a mawed crucible, eldritch structure that allows you to create potions of various effects, to do so transmute a table with a watertank"
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/void_phase,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
required_atoms = list(/obj/structure/reagent_dispensers/watertank,/obj/structure/table)
|
||||
result_atoms = list(/obj/structure/eldritch_crucible)
|
||||
|
||||
/datum/eldritch_knowledge/void_blade_upgrade
|
||||
name = "Seeking blade"
|
||||
gain_text = "Fleeting memories, fleeting feet. I can mark my way with the frozen blood upon the snow. Covered and forgotten."
|
||||
desc = "You can now use your blade on a distant marked target to move to them and attack them."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/voidpull)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/void_blade_upgrade/on_ranged_attack_eldritch_blade(atom/target, mob/user, click_parameters)
|
||||
. = ..()
|
||||
var/mob/living/carbon/carbon_human = user
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
var/datum/status_effect/eldritch/effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh) || human_target.has_status_effect(/datum/status_effect/eldritch/void)
|
||||
if(!effect)
|
||||
return
|
||||
var/dir = angle2dir(dir2angle(get_dir(user,human_target))+180)
|
||||
carbon_human.forceMove(get_step(human_target,dir))
|
||||
var/obj/item/melee/sickly_blade/blade = carbon_human.get_active_held_item()
|
||||
blade.melee_attack_chain(carbon_human,human_target,attackchain_flags = ATTACK_IGNORE_CLICKDELAY)
|
||||
|
||||
/datum/eldritch_knowledge/spell/voidpull
|
||||
name = "Void Pull"
|
||||
gain_text = "This entity calls itself The Aristocrat, I'm close to finishing what was started."
|
||||
desc = "You gain an ability that let's you pull people around you closer to you."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/void_pull
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/boogiewoogie,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/summon/rusty)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/spell/boogiewoogie
|
||||
name = "Void's Applause"
|
||||
gain_text = "The curtain is closing, and I'm certain that The Aristocrat is proud of me."
|
||||
desc = "With the clap of your hands, you can swap your position with someone within your vision."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/boogie_woogie
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/domain_expansion)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/spell/domain_expansion
|
||||
name = "Infinite Void"
|
||||
gain_text = "This world will be my stage, and nothing will be out of my reach."
|
||||
desc = "Gain the ability to mark a 7x7 area as your domain after a short delay. Creatures in your domain are slowed and branded with a void mark, allowing you to quickly teleport to them and slash them, further inhibiting their ability to move."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/domain_expansion
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/void_final)
|
||||
route = PATH_VOID
|
||||
|
||||
/datum/eldritch_knowledge/final/void_final
|
||||
name = "Waltz at the End of Time"
|
||||
desc = "Bring 3 corpses onto the transmutation rune. After you finish the ritual you will automatically silence people around you and will summon a snow storm around you."
|
||||
gain_text = "The world falls into darkness. I stand in an empty plane, small flakes of ice fall from the sky. The Aristocrat stands before me, he motions to me. We will play a waltz to the whispers of dying reality, as the world is destroyed before our eyes."
|
||||
cost = 5
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
route = PATH_VOID
|
||||
///soundloop for the void theme
|
||||
var/datum/looping_sound/void_loop/sound_loop
|
||||
///Reference to the ongoing voidstorm that surrounds the heretic
|
||||
var/datum/weather/void_storm/storm
|
||||
|
||||
/datum/eldritch_knowledge/final/void_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
var/mob/living/carbon/human/H = user
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch)
|
||||
H.physiology.brute_mod *= 0.5
|
||||
H.physiology.burn_mod *= 0.5
|
||||
ADD_TRAIT(H, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# The nobleman of void [H.real_name] has arrived, step along the Waltz that ends worlds! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
|
||||
sound_loop = new(list(user),TRUE,TRUE)
|
||||
return ..()
|
||||
|
||||
/datum/eldritch_knowledge/final/void_final/on_death()
|
||||
if(sound_loop)
|
||||
sound_loop.stop()
|
||||
if(storm)
|
||||
storm.end()
|
||||
QDEL_NULL(storm)
|
||||
|
||||
/datum/eldritch_knowledge/final/void_final/on_life(mob/user)
|
||||
. = ..()
|
||||
if(!finished)
|
||||
return
|
||||
|
||||
for(var/mob/living/carbon/livies in spiral_range(7,user)-user)
|
||||
if(IS_HERETIC_MONSTER(livies) || IS_HERETIC(livies))
|
||||
return
|
||||
livies.silent = clamp(livies.silent + 1, 0, 5)
|
||||
livies.adjust_bodytemperature(-20)
|
||||
|
||||
var/turf/turfie = get_turf(user)
|
||||
if(!isopenturf(turfie))
|
||||
return
|
||||
var/turf/open/open_turfie = turfie
|
||||
open_turfie.TakeTemperature(-20)
|
||||
|
||||
var/area/user_area = get_area(user)
|
||||
var/turf/user_turf = get_turf(user)
|
||||
|
||||
if(!storm)
|
||||
storm = new /datum/weather/void_storm(list(user_turf.z))
|
||||
storm.telegraph()
|
||||
|
||||
storm.area_type = user_area.type
|
||||
storm.impacted_areas = list(user_area)
|
||||
storm.update_areas()
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
switch(deconstruction_state)
|
||||
if(NUKESTATE_INTACT)
|
||||
if(istype(I, /obj/item/screwdriver/nuke))
|
||||
if(istype(I, /obj/item/screwdriver/nuke)) //Special case, cannot replace with tool_behavior
|
||||
to_chat(user, "<span class='notice'>You start removing [src]'s front panel's screws...</span>")
|
||||
if(I.use_tool(src, user, 60, volume=100))
|
||||
deconstruction_state = NUKESTATE_UNSCREWED
|
||||
|
||||
@@ -7,7 +7,21 @@
|
||||
earliest_start = 1 HOURS
|
||||
min_players = 20
|
||||
|
||||
|
||||
/datum/round_event_control/slaughter/canSpawnEvent()
|
||||
weight = initial(src.weight)
|
||||
var/list/allowed_turf_typecache = typecacheof(/turf/open) - typecacheof(/turf/open/space)
|
||||
var/list/allowed_z_cache = list()
|
||||
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
|
||||
allowed_z_cache[num2text(z)] = TRUE
|
||||
for(var/obj/effect/decal/cleanable/C in world)
|
||||
if(!C.loc || QDELETED(C))
|
||||
continue
|
||||
if(!C.can_bloodcrawl_in())
|
||||
continue
|
||||
if(!SSpersistence.IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE))
|
||||
continue
|
||||
weight += 0.05
|
||||
return ..()
|
||||
|
||||
/datum/round_event/ghost_role/slaughter
|
||||
minimum_required = 1
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
to_chat(user, "<span class='notice'>Picking up the swarmer may cause it to activate. You should be careful about this.</span>")
|
||||
|
||||
/obj/effect/mob_spawn/swarmer/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/screwdriver) && user.a_intent != INTENT_HARM)
|
||||
if(W.tool_behaviour == TOOL_SCREWDRIVER && user.a_intent != INTENT_HARM)
|
||||
user.visible_message("<span class='warning'>[usr.name] deactivates [src].</span>",
|
||||
"<span class='notice'>After some fiddling, you find a way to disable [src]'s power source.</span>",
|
||||
"<span class='italics'>You hear clicking.</span>")
|
||||
@@ -261,19 +261,26 @@
|
||||
S.DisIntegrate(src)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
var/isonshuttle = istype(get_area(src), /area/shuttle)
|
||||
for(var/turf/T in range(1, src))
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
|
||||
to_chat(S, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
S.target = null
|
||||
/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
|
||||
var/is_on_shuttle = istype(get_area(src), /area/shuttle)
|
||||
for(var/turf/turf_in_range in range(1, src))
|
||||
var/area/turf_area = get_area(turf_in_range)
|
||||
//Check for dangerous pressure differences
|
||||
// if (turf_in_range.return_turf_delta_p() > DANGEROUS_DELTA_P)
|
||||
// to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause an explosive pressure release. Aborting.</span>")
|
||||
// actor.target = null
|
||||
// return TRUE
|
||||
//Check if breaking this door will expose the station to space/planetary atmos
|
||||
if(isspaceturf(turf_in_range) || (!is_on_shuttle && (istype(turf_area, /area/shuttle) || istype(turf_area, /area/space))) || (is_on_shuttle && !istype(turf_area, /area/shuttle)))
|
||||
to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
actor.target = null
|
||||
return FALSE
|
||||
else if(istype(A, /area/engine/supermatter))
|
||||
to_chat(S, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
S.target = null
|
||||
//Check if this door is important in supermatter containment
|
||||
else if(istype(turf_area, /area/engineering/supermatter))
|
||||
to_chat(actor, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
actor.target = null
|
||||
return FALSE
|
||||
S.DisIntegrate(src)
|
||||
actor.DisIntegrate(src)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
@@ -342,31 +349,40 @@
|
||||
to_chat(S, "<span class='warning'>This bluespace source will be important to us later. Aborting.</span>")
|
||||
return FALSE
|
||||
|
||||
/turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
var/isonshuttle = istype(loc, /area/shuttle)
|
||||
for(var/turf/T in range(1, src))
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
|
||||
to_chat(S, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
S.target = null
|
||||
/turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
|
||||
var/is_on_shuttle = istype(loc, /area/shuttle)
|
||||
for(var/turf/turf_in_range in range(1, src))
|
||||
var/area/turf_area = get_area(turf_in_range)
|
||||
// if (turf_in_range.return_turf_delta_p() > DANGEROUS_DELTA_P)
|
||||
// to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause an explosive pressure release. Aborting.</span>")
|
||||
// actor.target = null
|
||||
// return TRUE
|
||||
if(isspaceturf(turf_in_range) || (!is_on_shuttle && (istype(turf_area, /area/shuttle) || istype(turf_area, /area/space))) || (is_on_shuttle && !istype(turf_area, /area/shuttle)))
|
||||
to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
actor.target = null
|
||||
return TRUE
|
||||
else if(istype(A, /area/engine/supermatter))
|
||||
to_chat(S, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
S.target = null
|
||||
else if(istype(turf_area, /area/engineering/supermatter))
|
||||
to_chat(actor, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
actor.target = null
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
var/isonshuttle = istype(get_area(src), /area/shuttle)
|
||||
for(var/turf/T in range(1, src))
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
|
||||
to_chat(S, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
S.target = null
|
||||
/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
|
||||
var/is_on_shuttle = istype(get_area(src), /area/shuttle)
|
||||
for(var/t in RANGE_TURFS(1, src))
|
||||
var/turf/turf_in_range = t
|
||||
var/area/turf_area = get_area(turf_in_range)
|
||||
// if (turf_in_range.return_turf_delta_p() > DANGEROUS_DELTA_P)
|
||||
// to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause an explosive pressure release. Aborting.</span>")
|
||||
// actor.target = null
|
||||
// return TRUE
|
||||
if(isspaceturf(turf_in_range) || (!is_on_shuttle && (istype(turf_area, /area/shuttle) || istype(turf_area, /area/space))) || (is_on_shuttle && !istype(turf_area, /area/shuttle)))
|
||||
to_chat(actor, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
actor.target = null
|
||||
return TRUE
|
||||
else if(istype(A, /area/engine/supermatter))
|
||||
to_chat(S, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
S.target = null
|
||||
else if(istype(turf_area, /area/engineering/supermatter))
|
||||
to_chat(actor, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
actor.target = null
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
|
||||
.=1
|
||||
var/permakill_prob = 20
|
||||
var/is_dynamic = FALSE
|
||||
var/datum/game_mode/dynamic/mode
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
is_dynamic = TRUE
|
||||
permakill_prob = max(0,mode.threat_level-50)
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
|
||||
@@ -20,11 +18,6 @@
|
||||
destroy_objective.owner = T.owner
|
||||
destroy_objective.find_target()
|
||||
T.add_objective(destroy_objective)
|
||||
else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN)))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = T.owner
|
||||
maroon_objective.find_target()
|
||||
T.add_objective(maroon_objective)
|
||||
else if(prob(permakill_prob))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
|
||||
@@ -41,11 +41,6 @@
|
||||
destroy_objective.owner = T.owner
|
||||
destroy_objective.find_target()
|
||||
T.add_objective(destroy_objective)
|
||||
else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN)))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = T.owner
|
||||
maroon_objective.find_target()
|
||||
T.add_objective(maroon_objective)
|
||||
else if(prob(max(0,assassin_prob-20)))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
|
||||
@@ -12,16 +12,10 @@
|
||||
mode = SSticker.mode
|
||||
assassin_prob = max(0,mode.threat_level-40)
|
||||
if(prob(assassin_prob))
|
||||
if(prob(assassin_prob))
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
kill_objective.find_target()
|
||||
T.add_objective(kill_objective)
|
||||
else
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = T.owner
|
||||
maroon_objective.find_target()
|
||||
T.add_objective(maroon_objective)
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
kill_objective.find_target()
|
||||
T.add_objective(kill_objective)
|
||||
else
|
||||
var/list/weights = list()
|
||||
weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
|
||||
|
||||
@@ -385,14 +385,10 @@
|
||||
/obj/item/warpwhistle/attack_self(mob/living/carbon/user)
|
||||
if(!istype(user) || on_cooldown)
|
||||
return
|
||||
var/turf/T = get_turf(user)
|
||||
var/area/A = get_area(user)
|
||||
if(!T || !A || A.noteleport)
|
||||
to_chat(user, "<span class='warning'>You play \the [src], yet no sound comes out of it... Looks like it won't work here.</span>")
|
||||
return
|
||||
on_cooldown = TRUE
|
||||
last_user = user
|
||||
playsound(T,'sound/magic/warpwhistle.ogg', 200, 1)
|
||||
var/turf/T = get_turf(user)
|
||||
playsound(T,'sound/magic/warpwhistle.ogg', 200, TRUE)
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOMOVE, src)
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOUSE, src)
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOPICKUP, src)
|
||||
@@ -400,6 +396,10 @@
|
||||
new /obj/effect/temp_visual/tornado(T)
|
||||
sleep(20)
|
||||
if(interrupted(user))
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOMOVE, src)
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOUSE, src)
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOPICKUP, src)
|
||||
user.update_mobility()
|
||||
return
|
||||
user.invisibility = INVISIBILITY_MAXIMUM
|
||||
user.status_flags |= GODMODE
|
||||
@@ -427,8 +427,7 @@
|
||||
if(interrupted(user))
|
||||
return
|
||||
on_cooldown = 2
|
||||
sleep(40)
|
||||
on_cooldown = 0
|
||||
addtimer(VARSET_CALLBACK(src, on_cooldown, 0), 4 SECONDS)
|
||||
|
||||
/obj/item/warpwhistle/Destroy()
|
||||
if(on_cooldown == 1 && last_user) //Flute got dunked somewhere in the teleport
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
var/hidden_underwear = FALSE
|
||||
var/hidden_undershirt = FALSE
|
||||
var/hidden_socks = FALSE
|
||||
var/arousal_rate = 1
|
||||
|
||||
//Mob procs
|
||||
/mob/living/carbon/human/verb/underwear_toggle()
|
||||
@@ -20,29 +21,34 @@
|
||||
return
|
||||
if(confirm == "Top")
|
||||
hidden_undershirt = !hidden_undershirt
|
||||
log_message("[hidden_undershirt ? "removed" : "put on" ] [p_their()] undershirt.", LOG_EMOTE)
|
||||
|
||||
if(confirm == "Bottom")
|
||||
hidden_underwear = !hidden_underwear
|
||||
log_message("[hidden_underwear ? "removed" : "put on"] [p_their()] underwear.", LOG_EMOTE)
|
||||
|
||||
if(confirm == "Socks")
|
||||
hidden_socks = !hidden_socks
|
||||
log_message("[hidden_socks ? "removed" : "put on"] [p_their()] socks.", LOG_EMOTE)
|
||||
|
||||
if(confirm == "All")
|
||||
var/on_off = (hidden_undershirt || hidden_underwear || hidden_socks) ? FALSE : TRUE
|
||||
hidden_undershirt = on_off
|
||||
hidden_underwear = on_off
|
||||
hidden_socks = on_off
|
||||
log_message("[on_off ? "removed" : "put on"] all [p_their()] undergarments.", LOG_EMOTE)
|
||||
|
||||
update_body(TRUE)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/adjust_arousal(strength,aphro = FALSE,maso = FALSE) // returns all genitals that were adjust
|
||||
/mob/living/carbon/human/proc/adjust_arousal(strength, cause = "manual toggle", aphro = FALSE,maso = FALSE) // returns all genitals that were adjust
|
||||
var/list/obj/item/organ/genital/genit_list = list()
|
||||
if(!client?.prefs.arousable || (aphro && (client?.prefs.cit_toggles & NO_APHRO)) || (maso && !HAS_TRAIT(src, TRAIT_MASO)))
|
||||
return // no adjusting made here
|
||||
var/enabling = strength > 0
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(strength*G.sensitivity))
|
||||
G.set_aroused_state(strength > 0)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(abs(strength)*G.sensitivity * arousal_rate))
|
||||
G.set_aroused_state(enabling,cause)
|
||||
G.update_appearance()
|
||||
if(G.aroused_state)
|
||||
genit_list += G
|
||||
@@ -64,6 +70,7 @@
|
||||
return
|
||||
var/turfing = isturf(target)
|
||||
G.generate_fluid(R)
|
||||
log_message("Climaxed using [G] with [target]", LOG_EMOTE)
|
||||
if(spill && R.total_volume >= 5)
|
||||
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
|
||||
if(!turfing)
|
||||
@@ -189,7 +196,7 @@
|
||||
return TRUE
|
||||
|
||||
//Here's the main proc itself
|
||||
/mob/living/carbon/human/proc/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
|
||||
/mob/living/carbon/human/proc/mob_climax(forced_climax=FALSE,cause = "") //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
|
||||
if(mb_cd_timer > world.time)
|
||||
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
|
||||
to_chat(src, "<span class='warning'>You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!</span>")
|
||||
@@ -202,32 +209,10 @@
|
||||
to_chat(src, "<span class='warning'>You can't do that while dead!</span>")
|
||||
return
|
||||
if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
|
||||
log_message("was forced to climax by [cause]",LOG_EMOTE)
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
|
||||
continue
|
||||
var/mob/living/partner
|
||||
var/check_target
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
if(G.is_exposed(worn_stuff))
|
||||
if(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
||||
if(isliving(pulling)) //Don't fuck objects
|
||||
check_target = pulling
|
||||
if(pulledby && !check_target) //prioritise pulled over pulledby
|
||||
if(isliving(pulledby))
|
||||
check_target = pulledby
|
||||
//Now we should have a partner, or else we have to come alone
|
||||
if(check_target)
|
||||
if(iscarbon(check_target)) //carbons can have clothes
|
||||
var/mob/living/carbon/C = check_target
|
||||
if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
|
||||
partner = C
|
||||
else //A cat is fine too
|
||||
partner = check_target
|
||||
if(partner) //Did they pass the clothing checks?
|
||||
mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
|
||||
continue //You've climaxed once with this organ, continue on
|
||||
//not exposed OR if no partner was found while exposed, climax alone
|
||||
mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
|
||||
//Now all genitals that could climax, have.
|
||||
//Since this was a forced climax, we do not need to continue with the other stuff
|
||||
@@ -272,7 +257,6 @@
|
||||
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
||||
if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
|
||||
mob_fill_container(picked_organ, fluid_container)
|
||||
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
|
||||
/mob/living/carbon/human/verb/climax_verb()
|
||||
|
||||
@@ -27,11 +27,12 @@
|
||||
if(do_update)
|
||||
update()
|
||||
|
||||
/obj/item/organ/genital/proc/set_aroused_state(new_state)
|
||||
/obj/item/organ/genital/proc/set_aroused_state(new_state,cause = "manual toggle")
|
||||
if(!(genital_flags & GENITAL_CAN_AROUSE))
|
||||
return FALSE
|
||||
if(!((HAS_TRAIT(owner,TRAIT_PERMABONER) && !new_state) || HAS_TRAIT(owner,TRAIT_NEVERBONER) && new_state))
|
||||
aroused_state = new_state
|
||||
owner.log_message("[src]'s arousal was [new_state ? "enabled" : "disabled"] due to [cause]", LOG_EMOTE)
|
||||
return aroused_state
|
||||
|
||||
/obj/item/organ/genital/proc/update()
|
||||
@@ -76,11 +77,19 @@
|
||||
if(GEN_VISIBLE_ALWAYS)
|
||||
genital_flags |= GENITAL_THROUGH_CLOTHES
|
||||
if(owner)
|
||||
owner.log_message("Exposed their [src]",LOG_EMOTE)
|
||||
owner.exposed_genitals += src
|
||||
if(GEN_VISIBLE_NO_CLOTHES)
|
||||
if(owner)
|
||||
owner.log_message("Hid their [src] under clothes only",LOG_EMOTE)
|
||||
if(GEN_VISIBLE_NO_UNDIES)
|
||||
genital_flags |= GENITAL_UNDIES_HIDDEN
|
||||
if(owner)
|
||||
owner.log_message("Hid their [src] under underwear",LOG_EMOTE)
|
||||
if(GEN_VISIBLE_NEVER)
|
||||
genital_flags |= GENITAL_HIDDEN
|
||||
if(owner)
|
||||
owner.log_message("Hid their [src] completely",LOG_EMOTE)
|
||||
|
||||
if(update && owner && ishuman(owner)) //recast to use update genitals proc
|
||||
var/mob/living/carbon/human/H = owner
|
||||
|
||||
@@ -262,70 +262,6 @@
|
||||
/obj/item/assembly/flash/armimplant/proc/cooldown()
|
||||
overheat = FALSE
|
||||
|
||||
/obj/item/assembly/flash/shield
|
||||
name = "strobe shield"
|
||||
desc = "A shield with a built in, high intensity light capable of blinding and disorienting suspects. Takes regular handheld flashes as bulbs."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "flashshield"
|
||||
item_state = "flashshield"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
force = 10
|
||||
throwforce = 5
|
||||
throw_speed = 2
|
||||
throw_range = 3
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
custom_materials = list(/datum/material/glass=7500, /datum/material/iron=1000)
|
||||
attack_verb = list("shoved", "bashed")
|
||||
block_chance = 50
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70)
|
||||
|
||||
/obj/item/assembly/flash/shield/flash_recharge(interval=10)
|
||||
if(times_used >= 4)
|
||||
burn_out()
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/assembly/flash/shield/attackby(obj/item/W, mob/user)
|
||||
if(istype(W, /obj/item/assembly/flash/handheld))
|
||||
var/obj/item/assembly/flash/handheld/flash = W
|
||||
if(flash.crit_fail)
|
||||
to_chat(user, "No sense replacing it with a broken bulb.")
|
||||
return
|
||||
else
|
||||
to_chat(user, "You begin to replace the bulb.")
|
||||
if(do_after(user, 20, target = src))
|
||||
if(flash.crit_fail || !flash || QDELETED(flash))
|
||||
return
|
||||
crit_fail = FALSE
|
||||
times_used = 0
|
||||
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
update_icon()
|
||||
flash.crit_fail = TRUE
|
||||
flash.update_icon()
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/assembly/flash/shield/update_icon(flash = FALSE)
|
||||
icon_state = "flashshield"
|
||||
item_state = "flashshield"
|
||||
|
||||
if(crit_fail)
|
||||
icon_state = "riot"
|
||||
item_state = "riot"
|
||||
else if(flash)
|
||||
icon_state = "flashshield_flash"
|
||||
item_state = "flashshield_flash"
|
||||
addtimer(CALLBACK(src, /atom/.proc/update_icon), 5)
|
||||
|
||||
if(holder)
|
||||
holder.update_icon()
|
||||
|
||||
/obj/item/assembly/flash/shield/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
activate()
|
||||
return ..()
|
||||
|
||||
//ported from tg - check to make sure it can't appear where it's not supposed to.
|
||||
/obj/item/assembly/flash/hypnotic
|
||||
desc = "A modified flash device, programmed to emit a sequence of subliminal flashes that can send a vulnerable target into a hypnotic trance."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/datum/asset_cache_item/New(name, file)
|
||||
if (!isfile(file))
|
||||
file = fcopy_rsc(file)
|
||||
|
||||
|
||||
hash = md5asfile(file) //icons sent to the rsc sometimes md5 incorrectly
|
||||
if (!hash)
|
||||
CRASH("invalid asset sent to asset cache")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/datum/asset/simple/tgui_common
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-common.chunk.js" = 'tgui/public/tgui-common.chunk.js',
|
||||
"tgui-common.bundle.js" = 'tgui/public/tgui-common.bundle.js',
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
@@ -47,9 +47,9 @@
|
||||
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
|
||||
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
|
||||
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
|
||||
// "borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
|
||||
// "robotact.gif" = 'icons/program_icons/robotact.gif'
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
|
||||
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
|
||||
"robotact.gif" = 'icons/program_icons/robotact.gif'
|
||||
)
|
||||
|
||||
/datum/asset/simple/radar_assets
|
||||
@@ -172,7 +172,6 @@
|
||||
/datum/asset/spritesheet/chat/register()
|
||||
InsertAll("emoji", 'icons/emoji.dmi')
|
||||
InsertAll("emoji", 'icons/emoji_32.dmi')
|
||||
|
||||
// pre-loading all lanugage icons also helps to avoid meta
|
||||
InsertAll("language", 'icons/misc/language.dmi')
|
||||
// catch languages which are pulling icons from another file
|
||||
@@ -190,7 +189,7 @@
|
||||
)
|
||||
|
||||
/datum/asset/simple/namespaced/common
|
||||
assets = list("padlock.png" = 'html/padlock.png')
|
||||
assets = list("padlock.png" = 'html/padlock.png')
|
||||
parents = list("common.css" = 'html/browser/common.css')
|
||||
|
||||
/datum/asset/simple/permissions
|
||||
@@ -222,7 +221,7 @@
|
||||
"boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
|
||||
"boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
|
||||
)
|
||||
/*
|
||||
|
||||
/datum/asset/spritesheet/simple/achievements
|
||||
name ="achievements"
|
||||
assets = list(
|
||||
@@ -233,6 +232,7 @@
|
||||
"bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
|
||||
"colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
|
||||
"hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
|
||||
"drake" = 'icons/UI_Icons/Achievements/Boss/drake.png',
|
||||
"legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
|
||||
"miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
|
||||
"swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
|
||||
@@ -246,32 +246,23 @@
|
||||
"clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
|
||||
"clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
|
||||
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
|
||||
"longshift" = 'icons/UI_Icons/Achievements/Misc/longshift.png',
|
||||
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
|
||||
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
|
||||
"ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
|
||||
"fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
|
||||
"rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
|
||||
"voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
|
||||
"toolbox_soul" = 'icons/UI_Icons/Achievements/Misc/toolbox_soul.png',
|
||||
"chem_tut" = 'icons/UI_Icons/Achievements/Misc/chem_tut.png',
|
||||
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
|
||||
"assistant" = 'icons/UI_Icons/Achievements/Mafia/assistant.png',
|
||||
"changeling" = 'icons/UI_Icons/Achievements/Mafia/changeling.png',
|
||||
"chaplain" = 'icons/UI_Icons/Achievements/Mafia/chaplain.png',
|
||||
"clown" = 'icons/UI_Icons/Achievements/Mafia/clown.png',
|
||||
"detective" = 'icons/UI_Icons/Achievements/Mafia/detective.png',
|
||||
"fugitive" = 'icons/UI_Icons/Achievements/Mafia/fugitive.png',
|
||||
"mafia" = 'icons/UI_Icons/Achievements/Mafia/mafia.png',
|
||||
"town" = 'icons/UI_Icons/Achievements/Mafia/town.png',
|
||||
"neutral" = 'icons/UI_Icons/Achievements/Mafia/neutral.png',
|
||||
"hated" = 'icons/UI_Icons/Achievements/Mafia/hated.png',
|
||||
"hop" = 'icons/UI_Icons/Achievements/Mafia/hop.png',
|
||||
"lawyer" = 'icons/UI_Icons/Achievements/Mafia/lawyer.png',
|
||||
"md" = 'icons/UI_Icons/Achievements/Mafia/md.png',
|
||||
"nightmare" = 'icons/UI_Icons/Achievements/Mafia/nightmare.png',
|
||||
"obsessed" = 'icons/UI_Icons/Achievements/Mafia/obsessed.png',
|
||||
"psychologist" = 'icons/UI_Icons/Achievements/Mafia/psychologist.png',
|
||||
"thoughtfeeder" = 'icons/UI_Icons/Achievements/Mafia/thoughtfeeder.png',
|
||||
"traitor" = 'icons/UI_Icons/Achievements/Mafia/traitor.png',
|
||||
"basemafia" ='icons/UI_Icons/Achievements/basemafia.png',
|
||||
"frenching" = 'icons/UI_Icons/Achievements/Misc/frenchingthebubble.png'
|
||||
)
|
||||
*/
|
||||
|
||||
/datum/asset/spritesheet/simple/minesweeper
|
||||
name = "minesweeper"
|
||||
@@ -317,28 +308,28 @@
|
||||
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
|
||||
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
|
||||
)
|
||||
/*
|
||||
/datum/asset/spritesheet/simple/condiments
|
||||
name = "condiments"
|
||||
assets = list(
|
||||
CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
|
||||
"enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
|
||||
"flour" = 'icons/UI_Icons/Condiments/flour.png',
|
||||
"mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
|
||||
"milk" = 'icons/UI_Icons/Condiments/milk.png',
|
||||
"blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
|
||||
"rice" = 'icons/UI_Icons/Condiments/rice.png',
|
||||
"sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
|
||||
"soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
|
||||
"soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
|
||||
"sugar" = 'icons/UI_Icons/Condiments/sugar.png',
|
||||
"ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
|
||||
"capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
|
||||
"frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
|
||||
"bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
|
||||
"cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
|
||||
)
|
||||
*/
|
||||
|
||||
// /datum/asset/spritesheet/simple/condiments
|
||||
// name = "condiments"
|
||||
// assets = list(
|
||||
// CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
|
||||
// "enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
|
||||
// "flour" = 'icons/UI_Icons/Condiments/flour.png',
|
||||
// "mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
|
||||
// "milk" = 'icons/UI_Icons/Condiments/milk.png',
|
||||
// "blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
|
||||
// "rice" = 'icons/UI_Icons/Condiments/rice.png',
|
||||
// "sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
|
||||
// "soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
|
||||
// "soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
|
||||
// "sugar" = 'icons/UI_Icons/Condiments/sugar.png',
|
||||
// "ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
|
||||
// "capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
|
||||
// "frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
|
||||
// "bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
|
||||
// "cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
|
||||
// )
|
||||
|
||||
//this exists purely to avoid meta by pre-loading all language icons.
|
||||
/datum/asset/language/register()
|
||||
for(var/path in typesof(/datum/language))
|
||||
@@ -485,7 +476,7 @@
|
||||
|
||||
/datum/asset/simple/orbit
|
||||
assets = list(
|
||||
"ghost.png" = 'html/ghost.png'
|
||||
"ghost.png" = 'html/ghost.png'
|
||||
)
|
||||
|
||||
/datum/asset/simple/vv
|
||||
@@ -521,7 +512,8 @@
|
||||
var/list/portrait = p
|
||||
var/png = "data/paintings/[tab]/[portrait["md5"]].png"
|
||||
if(fexists(png))
|
||||
assets[portrait["title"]] = png
|
||||
var/asset_name = "[tab]_[portrait["md5"]]"
|
||||
assets[asset_name] = png
|
||||
..() //this is where it registers all these assets we added to the list
|
||||
|
||||
/datum/asset/simple/portraits/library
|
||||
@@ -537,3 +529,27 @@
|
||||
assets = list(
|
||||
"safe_dial.png" = 'html/safe_dial.png'
|
||||
)
|
||||
|
||||
// /datum/asset/spritesheet/fish
|
||||
// name = "fish"
|
||||
|
||||
// /datum/asset/spritesheet/fish/register()
|
||||
// for (var/path in subtypesof(/datum/aquarium_behaviour/fish))
|
||||
// var/datum/aquarium_behaviour/fish/fish_type = path
|
||||
// var/fish_icon = initial(fish_type.icon)
|
||||
// var/fish_icon_state = initial(fish_type.icon_state)
|
||||
// var/id = sanitize_css_class_name("[fish_icon][fish_icon_state]")
|
||||
// if(sprites[id]) //no dupes
|
||||
// continue
|
||||
// Insert(id, fish_icon, fish_icon_state)
|
||||
// ..()
|
||||
|
||||
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
|
||||
/proc/sanitize_css_class_name(name)
|
||||
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
|
||||
return replacetext(name, regex, "")
|
||||
|
||||
/datum/asset/simple/tutorial_advisors
|
||||
assets = list(
|
||||
"chem_help_advisor.gif" = 'icons/UI_Icons/Advisors/chem_help_advisor.gif',
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ Call .get_url_mappings() to get an associated list with the urls your assets can
|
||||
|
||||
See the documentation for `/datum/asset_transport` for the backend api the asset datums utilize.
|
||||
|
||||
The global variable `SSassets.transport` contains the currently configured transport.
|
||||
The global variable `SSassets.transport` contains the currently configured transport.
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,6 @@ The global variable `SSassets.transport` contains the currently configured trans
|
||||
|
||||
Because byond browse() calls use non-blocking queues, if your code uses output() (which bypasses all of these queues) to invoke javascript functions you will need to first have the javascript announce to the server it has loaded before trying to invoke js functions.
|
||||
|
||||
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
|
||||
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
|
||||
|
||||
CSS files that use url() can be made to use the CDN without needing to rewrite all url() calls in code by using the namespaced helper datum. See the documentation for `/datum/asset/simple/namespaced` for details.
|
||||
|
||||
@@ -359,3 +359,28 @@ get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles()
|
||||
to_chat(src, "Total time (new gas mixture): [total_time]ms")
|
||||
to_chat(src, "Operations per second: [100000 / (total_time/1000)]")
|
||||
*/
|
||||
|
||||
/// Releases gas from src to output air. This means that it can not transfer air to gas mixture with higher pressure.
|
||||
/// a global proc due to rustmos
|
||||
/proc/release_gas_to(datum/gas_mixture/input_air, datum/gas_mixture/output_air, target_pressure)
|
||||
var/output_starting_pressure = output_air.return_pressure()
|
||||
var/input_starting_pressure = input_air.return_pressure()
|
||||
|
||||
if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
|
||||
//No need to pump gas if target is already reached or input pressure is too low
|
||||
//Need at least 10 KPa difference to overcome friction in the mechanism
|
||||
return FALSE
|
||||
|
||||
//Calculate necessary moles to transfer using PV = nRT
|
||||
if((input_air.total_moles() > 0) && (input_air.return_temperature()>0))
|
||||
var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
|
||||
//Can not have a pressure delta that would cause output_pressure > input_pressure
|
||||
|
||||
var/transfer_moles = pressure_delta*output_air.return_volume()/(input_air.return_temperature() * R_IDEAL_GAS_EQUATION)
|
||||
|
||||
//Actually transfer the gas
|
||||
var/datum/gas_mixture/removed = input_air.remove(transfer_moles)
|
||||
output_air.merge(removed)
|
||||
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -256,6 +256,8 @@
|
||||
|
||||
/datum/gas_reaction/fusion/react(datum/gas_mixture/air, datum/holder)
|
||||
var/turf/open/location
|
||||
if (isopenturf(holder))
|
||||
return
|
||||
if (istype(holder,/datum/pipeline)) //Find the tile the reaction is occuring on, or a random part of the network if it's a pipenet.
|
||||
var/datum/pipeline/fusion_pipenet = holder
|
||||
location = get_turf(pick(fusion_pipenet.members))
|
||||
@@ -356,7 +358,7 @@
|
||||
/datum/gas/oxygen = 20,
|
||||
/datum/gas/nitrogen = 20,
|
||||
/datum/gas/nitrous_oxide = 5,
|
||||
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST*400
|
||||
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST*25
|
||||
)
|
||||
|
||||
/datum/gas_reaction/nitrylformation/react(datum/gas_mixture/air)
|
||||
@@ -367,8 +369,8 @@
|
||||
var/energy_used = heat_efficency*NITRYL_FORMATION_ENERGY
|
||||
if ((air.get_moles(/datum/gas/oxygen) - heat_efficency < 0 )|| (air.get_moles(/datum/gas/nitrogen) - heat_efficency < 0)) //Shouldn't produce gas from nothing.
|
||||
return NO_REACTION
|
||||
air.adjust_moles(/datum/gas/oxygen, heat_efficency)
|
||||
air.adjust_moles(/datum/gas/nitrogen, heat_efficency)
|
||||
air.adjust_moles(/datum/gas/oxygen, -heat_efficency)
|
||||
air.adjust_moles(/datum/gas/nitrogen, -heat_efficency)
|
||||
air.adjust_moles(/datum/gas/nitryl, heat_efficency*2)
|
||||
|
||||
if(energy_used > 0)
|
||||
@@ -497,7 +499,7 @@
|
||||
min_requirements = list(
|
||||
/datum/gas/nitrogen = 10,
|
||||
/datum/gas/tritium = 5,
|
||||
"TEMP" = 5000000)
|
||||
"ENER" = NOBLIUM_FORMATION_ENERGY)
|
||||
|
||||
/datum/gas_reaction/nobliumformation/react(datum/gas_mixture/air)
|
||||
var/old_heat_capacity = air.heat_capacity()
|
||||
|
||||
@@ -304,7 +304,7 @@
|
||||
"danger_level" = cur_tlv.get_danger_level(environment.get_moles(gas_id) * partial_pressure)
|
||||
))
|
||||
|
||||
if(!locked || hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE))
|
||||
if(!locked || hasSiliconAccessInArea(user, PRIVILEGES_SILICON|PRIVILEGES_DRONE))
|
||||
data["vents"] = list()
|
||||
for(var/id_tag in A.air_vent_names)
|
||||
var/long_name = A.air_vent_names[id_tag]
|
||||
@@ -385,13 +385,13 @@
|
||||
if(..() || buildstage != 2)
|
||||
return
|
||||
var/silicon_access = hasSiliconAccessInArea(usr)
|
||||
var/bot_priviledges = silicon_access || (usr.silicon_privileges & PRIVILEDGES_DRONE)
|
||||
if((locked && !bot_priviledges) || (silicon_access && aidisabled))
|
||||
var/bot_privileges = silicon_access || (usr.silicon_privileges & PRIVILEGES_DRONE)
|
||||
if((locked && !bot_privileges) || (silicon_access && aidisabled))
|
||||
return
|
||||
var/device_id = params["id_tag"]
|
||||
switch(action)
|
||||
if("lock")
|
||||
if(bot_priviledges && !wires.is_cut(WIRE_IDSCAN))
|
||||
if(bot_privileges && !wires.is_cut(WIRE_IDSCAN))
|
||||
locked = !locked
|
||||
. = TRUE
|
||||
if("power", "toggle_filter", "widenet", "scrubbing")
|
||||
@@ -762,14 +762,14 @@
|
||||
/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params)
|
||||
switch(buildstage)
|
||||
if(2)
|
||||
if(istype(W, /obj/item/wirecutters) && panel_open && wires.is_all_cut())
|
||||
if(W.tool_behaviour == TOOL_WIRECUTTER && panel_open && wires.is_all_cut())
|
||||
W.play_tool_sound(src)
|
||||
to_chat(user, "<span class='notice'>You cut the final wires.</span>")
|
||||
new /obj/item/stack/cable_coil(loc, 5)
|
||||
buildstage = 1
|
||||
update_icon()
|
||||
return
|
||||
else if(istype(W, /obj/item/screwdriver)) // Opening that Air Alarm up.
|
||||
else if(W.tool_behaviour == TOOL_SCREWDRIVER) // Opening that Air Alarm up.
|
||||
W.play_tool_sound(src)
|
||||
panel_open = !panel_open
|
||||
to_chat(user, "<span class='notice'>The wires have been [panel_open ? "exposed" : "unexposed"].</span>")
|
||||
@@ -781,7 +781,7 @@
|
||||
wires.interact(user)
|
||||
return
|
||||
if(1)
|
||||
if(istype(W, /obj/item/crowbar))
|
||||
if(W.tool_behaviour == TOOL_CROWBAR)
|
||||
user.visible_message("[user.name] removes the electronics from [src.name].",\
|
||||
"<span class='notice'>You start prying out the circuit...</span>")
|
||||
W.play_tool_sound(src)
|
||||
@@ -832,7 +832,7 @@
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/wrench))
|
||||
if(W.tool_behaviour == TOOL_WRENCH)
|
||||
to_chat(user, "<span class='notice'>You detach \the [src] from the wall.</span>")
|
||||
W.play_tool_sound(src)
|
||||
new /obj/item/wallframe/airalarm( user.loc )
|
||||
|
||||
@@ -55,26 +55,7 @@ Passive gate is similar to the regular pump except:
|
||||
|
||||
var/datum/gas_mixture/air1 = airs[1]
|
||||
var/datum/gas_mixture/air2 = airs[2]
|
||||
|
||||
var/output_starting_pressure = air2.return_pressure()
|
||||
var/input_starting_pressure = air1.return_pressure()
|
||||
|
||||
if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
|
||||
//No need to pump gas if target is already reached or input pressure is too low
|
||||
//Need at least 10 KPa difference to overcome friction in the mechanism
|
||||
return
|
||||
|
||||
//Calculate necessary moles to transfer using PV = nRT
|
||||
if((air1.total_moles() > 0) && (air1.return_temperature()>0))
|
||||
var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
|
||||
//Can not have a pressure delta that would cause output_pressure > input_pressure
|
||||
|
||||
var/transfer_moles = pressure_delta*air2.return_volume()/(air1.return_temperature() * R_IDEAL_GAS_EQUATION)
|
||||
|
||||
//Actually transfer the gas
|
||||
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
|
||||
air2.merge(removed)
|
||||
|
||||
if(release_gas_to(air1, air2, target_pressure))
|
||||
update_parents()
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
desc = "Very useful for filtering gasses."
|
||||
|
||||
can_unwrench = TRUE
|
||||
|
||||
var/transfer_rate = MAX_TRANSFER_RATE
|
||||
var/filter_type = null
|
||||
var/frequency = 0
|
||||
|
||||
@@ -326,7 +326,7 @@
|
||||
|| default_deconstruction_crowbar(I))
|
||||
update_icon()
|
||||
return
|
||||
else if(istype(I, /obj/item/screwdriver))
|
||||
else if(I.tool_behaviour == TOOL_SCREWDRIVER)
|
||||
to_chat(user, "<span class='notice'>You can't access the maintenance panel while the pod is " \
|
||||
+ (on ? "active" : (occupant ? "full" : "open")) + ".</span>")
|
||||
return
|
||||
@@ -454,8 +454,10 @@
|
||||
return G.return_temperature()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W)
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/W)
|
||||
. = ..()
|
||||
if(!W.tool_behaviour == TOOL_WRENCH)
|
||||
return
|
||||
if(.)
|
||||
SetInitDirections()
|
||||
var/obj/machinery/atmospherics/node = nodes[1]
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
/obj/machinery/atmospherics/components/unary/tank
|
||||
icon = 'icons/obj/atmospherics/pipes/pressure_tank.dmi'
|
||||
icon_state = "generic"
|
||||
|
||||
name = "pressure tank"
|
||||
desc = "A large vessel containing pressurized gas."
|
||||
|
||||
max_integrity = 800
|
||||
density = TRUE
|
||||
layer = ABOVE_WINDOW_LAYER
|
||||
plane = GAME_PLANE
|
||||
pipe_flags = PIPING_ONE_PER_TURF
|
||||
|
||||
var/volume = 10000 //in liters
|
||||
/// The typepath of the gas this tank should be filled with.
|
||||
var/gas_type = 0
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/New()
|
||||
@@ -20,6 +23,7 @@
|
||||
if(gas_type)
|
||||
air_contents.set_moles(AIR_CONTENTS)
|
||||
name = "[name] ([GLOB.meta_gas_names[gas_type]])"
|
||||
setPipingLayer(piping_layer)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/air
|
||||
icon_state = "grey"
|
||||
@@ -38,15 +42,71 @@
|
||||
icon_state = "orange"
|
||||
gas_type = /datum/gas/plasma
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/oxygen
|
||||
icon_state = "blue"
|
||||
gas_type = /datum/gas/oxygen
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/nitrogen
|
||||
icon_state = "red"
|
||||
gas_type = /datum/gas/nitrogen
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/nitrous_oxide
|
||||
/obj/machinery/atmospherics/components/unary/tank/oxygen
|
||||
icon_state = "blue"
|
||||
gas_type = /datum/gas/oxygen
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/nitrous
|
||||
icon_state = "red_white"
|
||||
gas_type = /datum/gas/nitrous_oxide
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/bz
|
||||
gas_type = /datum/gas/bz
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/freon
|
||||
// icon_state = "blue"
|
||||
// gas_type = /datum/gas/freon
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/halon
|
||||
// icon_state = "blue"
|
||||
// gas_type = /datum/gas/halon
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/healium
|
||||
// icon_state = "red"
|
||||
// gas_type = /datum/gas/healium
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/hydrogen
|
||||
// icon_state = "grey"
|
||||
// gas_type = /datum/gas/hydrogen
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/hypernoblium
|
||||
icon_state = "blue"
|
||||
gas_type = /datum/gas/hypernoblium
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/miasma
|
||||
gas_type = /datum/gas/miasma
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/nitryl
|
||||
gas_type = /datum/gas/nitryl
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/pluoxium
|
||||
icon_state = "blue"
|
||||
gas_type = /datum/gas/pluoxium
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/proto_nitrate
|
||||
// icon_state = "red"
|
||||
// gas_type = /datum/gas/proto_nitrate
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/stimulum
|
||||
icon_state = "red"
|
||||
gas_type = /datum/gas/stimulum
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/tritium
|
||||
gas_type = /datum/gas/tritium
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/tank/water_vapor
|
||||
icon_state = "grey"
|
||||
gas_type = /datum/gas/water_vapor
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/zauker
|
||||
// gas_type = /datum/gas/zauker
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/helium
|
||||
// gas_type = /datum/gas/helium
|
||||
|
||||
// /obj/machinery/atmospherics/components/unary/tank/antinoblium
|
||||
// gas_type = /datum/gas/antinoblium
|
||||
|
||||
@@ -5,22 +5,28 @@
|
||||
desc = "A canister for the storage of gas."
|
||||
icon_state = "yellow"
|
||||
density = TRUE
|
||||
|
||||
var/valve_open = FALSE
|
||||
var/obj/machinery/atmospherics/components/binary/passive_gate/pump
|
||||
var/release_log = ""
|
||||
|
||||
volume = 1000
|
||||
var/filled = 0.5
|
||||
var/gas_type
|
||||
var/release_pressure = ONE_ATMOSPHERE
|
||||
var/can_max_release_pressure = (ONE_ATMOSPHERE * 10)
|
||||
var/can_min_release_pressure = (ONE_ATMOSPHERE / 10)
|
||||
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50)
|
||||
max_integrity = 250
|
||||
integrity_failure = 0.4
|
||||
pressure_resistance = 7 * ONE_ATMOSPHERE
|
||||
|
||||
var/valve_open = FALSE
|
||||
var/release_log = ""
|
||||
|
||||
var/filled = 0.5
|
||||
var/gas_type
|
||||
|
||||
var/release_pressure = ONE_ATMOSPHERE
|
||||
var/can_max_release_pressure = (ONE_ATMOSPHERE * 10)
|
||||
var/can_min_release_pressure = (ONE_ATMOSPHERE / 10)
|
||||
|
||||
// this removes atmos fusion cans**
|
||||
///Max amount of heat allowed inside of the canister before it starts to melt (different tiers have different limits)
|
||||
// var/heat_limit = 5000
|
||||
///Max amount of pressure allowed inside of the canister before it starts to break (different tiers have different limits)
|
||||
// var/pressure_limit = 50000
|
||||
|
||||
var/temperature_resistance = 1000 + T0C
|
||||
var/starter_temp
|
||||
// Prototype vars
|
||||
@@ -32,6 +38,8 @@
|
||||
var/maximum_timer_set = 300
|
||||
var/timing = FALSE
|
||||
var/restricted = FALSE
|
||||
///Set the tier of the canister and overlay used
|
||||
// var/mode = CANISTER_TIER_1
|
||||
req_access = list()
|
||||
|
||||
var/update = 0
|
||||
@@ -186,7 +194,6 @@
|
||||
can_min_release_pressure = (ONE_ATMOSPHERE / 30)
|
||||
prototype = TRUE
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/proto/default/oxygen
|
||||
name = "prototype canister"
|
||||
desc = "A prototype canister for a prototype bike, what could go wrong?"
|
||||
@@ -197,25 +204,17 @@
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture)
|
||||
..()
|
||||
|
||||
if(existing_mixture)
|
||||
air_contents.copy_from(existing_mixture)
|
||||
else
|
||||
create_gas()
|
||||
pump = new(src, FALSE)
|
||||
pump.on = TRUE
|
||||
pump.stat = 0
|
||||
SSair.add_to_rebuild_queue(pump)
|
||||
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/Destroy()
|
||||
qdel(pump)
|
||||
pump = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/proc/create_gas()
|
||||
if(gas_type)
|
||||
// air_contents.add_gas(gas_type)
|
||||
if(starter_temp)
|
||||
air_contents.set_temperature(starter_temp)
|
||||
air_contents.set_moles(gas_type,(maximum_pressure * filled) * air_contents.return_volume() / (R_IDEAL_GAS_EQUATION * air_contents.return_temperature()))
|
||||
@@ -266,14 +265,17 @@
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
return FALSE
|
||||
|
||||
if(stat & BROKEN)
|
||||
if(!I.tool_start_check(user, amount=0))
|
||||
return TRUE
|
||||
to_chat(user, "<span class='notice'>You begin cutting [src] apart...</span>")
|
||||
if(I.use_tool(src, user, 30, volume=50))
|
||||
deconstruct(TRUE)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You cannot slice [src] apart when it isn't broken.</span>")
|
||||
if(!I.tool_start_check(user, amount=0))
|
||||
return TRUE
|
||||
var/pressure = air_contents.return_pressure()
|
||||
if(pressure > 300)
|
||||
to_chat(user, "<span class='alert'>The pressure gauge on \the [src] indicates a high pressure inside... maybe you want to reconsider?</span>")
|
||||
to_chat(user, "<span class='notice'>You begin cutting \the [src] apart...</span>")
|
||||
if(I.use_tool(src, user, 3 SECONDS, volume=50))
|
||||
to_chat(user, "<span class='notice'>You cut \the [src] apart.</span>")
|
||||
deconstruct(TRUE)
|
||||
message_admins("[src] deconstructed by [ADMIN_LOOKUPFLW(user)]")
|
||||
log_game("[src] deconstructed by [key_name(user)]")
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -316,19 +318,20 @@
|
||||
if(timing && valve_timer < world.time)
|
||||
valve_open = !valve_open
|
||||
timing = FALSE
|
||||
if(!valve_open)
|
||||
pump.airs[1] = null
|
||||
pump.airs[2] = null
|
||||
return
|
||||
if(valve_open)
|
||||
var/turf/T = get_turf(src)
|
||||
var/datum/gas_mixture/target_air = holding ? holding.air_contents : T.return_air()
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
pump.airs[1] = air_contents
|
||||
pump.airs[2] = holding ? holding.air_contents : T.return_air()
|
||||
pump.target_pressure = release_pressure
|
||||
if(release_gas_to(air_contents, target_air, release_pressure) && !holding)
|
||||
air_update_turf()
|
||||
|
||||
pump.process_atmos() // Pump gas.
|
||||
if(!holding)
|
||||
air_update_turf() // Update the environment if needed.
|
||||
// var/our_pressure = air_contents.return_pressure()
|
||||
// var/our_temperature = air_contents.return_temperature()
|
||||
|
||||
///function used to check the limit of the canisters and also set the amount of damage that the canister can receive, if the heat and pressure are way higher than the limit the more damage will be done
|
||||
// currently unused
|
||||
// if(our_temperature > heat_limit || our_pressure > pressure_limit)
|
||||
// take_damage(clamp((our_temperature/heat_limit) * (our_pressure/pressure_limit) * delta_time * 2, 5, 50), BURN, 0)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/ui_state(mob/user)
|
||||
@@ -340,35 +343,48 @@
|
||||
ui = new(user, src, "Canister", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/ui_static_data(mob/user)
|
||||
return list(
|
||||
"defaultReleasePressure" = round(CAN_DEFAULT_RELEASE_PRESSURE),
|
||||
"minReleasePressure" = round(can_min_release_pressure),
|
||||
"maxReleasePressure" = round(can_max_release_pressure),
|
||||
"pressureLimit" = round(1e14),
|
||||
"holdingTankLeakPressure" = round(TANK_LEAK_PRESSURE),
|
||||
"holdingTankFragPressure" = round(TANK_FRAGMENT_PRESSURE)
|
||||
)
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/ui_data()
|
||||
var/data = list()
|
||||
data["portConnected"] = connected_port ? 1 : 0
|
||||
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
|
||||
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
|
||||
data["defaultReleasePressure"] = round(CAN_DEFAULT_RELEASE_PRESSURE)
|
||||
data["minReleasePressure"] = round(can_min_release_pressure)
|
||||
data["maxReleasePressure"] = round(can_max_release_pressure)
|
||||
data["valveOpen"] = valve_open ? 1 : 0
|
||||
. = list(
|
||||
"portConnected" = !!connected_port,
|
||||
"tankPressure" = round(air_contents.return_pressure()),
|
||||
"releasePressure" = round(release_pressure),
|
||||
"valveOpen" = !!valve_open,
|
||||
"isPrototype" = !!prototype,
|
||||
"hasHoldingTank" = !!holding
|
||||
)
|
||||
|
||||
data["isPrototype"] = prototype ? 1 : 0
|
||||
if (prototype)
|
||||
data["restricted"] = restricted
|
||||
data["timing"] = timing
|
||||
data["time_left"] = get_time_left()
|
||||
data["timer_set"] = timer_set
|
||||
data["timer_is_not_default"] = timer_set != default_timer_set
|
||||
data["timer_is_not_min"] = timer_set != minimum_timer_set
|
||||
data["timer_is_not_max"] = timer_set != maximum_timer_set
|
||||
. += list(
|
||||
"restricted" = restricted,
|
||||
"timing" = timing,
|
||||
"time_left" = get_time_left(),
|
||||
"timer_set" = timer_set,
|
||||
"timer_is_not_default" = timer_set != default_timer_set,
|
||||
"timer_is_not_min" = timer_set != minimum_timer_set,
|
||||
"timer_is_not_max" = timer_set != maximum_timer_set
|
||||
)
|
||||
|
||||
data["hasHoldingTank"] = holding ? 1 : 0
|
||||
if (holding)
|
||||
data["holdingTank"] = list()
|
||||
data["holdingTank"]["name"] = holding.name
|
||||
data["holdingTank"]["tankPressure"] = round(holding.air_contents.return_pressure())
|
||||
return data
|
||||
. += list(
|
||||
"holdingTank" = list(
|
||||
"name" = holding.name,
|
||||
"tankPressure" = round(holding.air_contents.return_pressure())
|
||||
)
|
||||
)
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/ui_act(action, params)
|
||||
if(..())
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("relabel")
|
||||
@@ -377,6 +393,7 @@
|
||||
var/newtype = label2types[label]
|
||||
if(newtype)
|
||||
var/obj/machinery/portable_atmospherics/canister/replacement = newtype
|
||||
investigate_log("was relabelled to [initial(replacement.name)] by [key_name(usr)].", INVESTIGATE_ATMOS)
|
||||
name = initial(replacement.name)
|
||||
desc = initial(replacement.desc)
|
||||
icon_state = initial(replacement.icon_state)
|
||||
@@ -458,9 +475,8 @@
|
||||
if("eject")
|
||||
if(holding)
|
||||
if(valve_open)
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the <span class='boldannounce'>air</span><br>.")
|
||||
investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the <span class='boldannounce'>air</span><br>", INVESTIGATE_ATMOS)
|
||||
holding.forceMove(get_turf(src))
|
||||
holding = null
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the <span class='boldannounce'>air</span>.")
|
||||
investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the <span class='boldannounce'>air</span>.", INVESTIGATE_ATMOS)
|
||||
replace_tank(usr, FALSE)
|
||||
. = TRUE
|
||||
update_icon()
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
to_chat(user, "<span class='notice'>[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].</span>")
|
||||
replace_tank(user, FALSE, T)
|
||||
update_icon()
|
||||
else if(istype(W, /obj/item/wrench))
|
||||
else if(W.tool_behaviour == TOOL_WRENCH)
|
||||
if(!(stat & BROKEN))
|
||||
if(connected_port)
|
||||
disconnect()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user