Revert "Revert "PLASE""

This reverts commit 00601a0c87.
This commit is contained in:
Fermi
2019-11-24 03:04:52 +00:00
parent 25fe5ec14b
commit aeb8606ce0
2145 changed files with 5258 additions and 1387322 deletions
-38
View File
@@ -1,38 +0,0 @@
/datum/ntnet_service
var/name = "Unidentified Network Service"
var/id
var/list/networks_by_id = list() //Yes we support multinetwork services!
/datum/ntnet_service/New()
var/datum/component/ntnet_interface/N = AddComponent(/datum/component/ntnet_interface, id, name, FALSE)
id = N.hardware_id
/datum/ntnet_service/Destroy()
for(var/i in networks_by_id)
var/datum/ntnet/N = i
disconnect(N, TRUE)
networks_by_id = null
return ..()
/datum/ntnet_service/proc/connect(datum/ntnet/net)
if(!istype(net))
return FALSE
var/datum/component/ntnet_interface/interface = GetComponent(/datum/component/ntnet_interface)
if(!interface.register_connection(net))
return FALSE
if(!net.register_service(src))
interface.unregister_connection(net)
return FALSE
networks_by_id[net.network_id] = net
return TRUE
/datum/ntnet_service/proc/disconnect(datum/ntnet/net, force = FALSE)
if(!istype(net) || (!net.unregister_service(src) && !force))
return FALSE
var/datum/component/ntnet_interface/interface = GetComponent(/datum/component/ntnet_interface)
interface.unregister_connection(net)
networks_by_id -= net.network_id
return TRUE
/datum/ntnet_service/proc/ntnet_intercept(datum/netdata/data, datum/ntnet/net, datum/component/ntnet_interface/sender)
return
-987
View File
@@ -1,987 +0,0 @@
////////////////////////////////
/proc/message_admins(msg)
msg = "<span class=\"admin\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins, msg)
/proc/relay_msg_admins(msg)
msg = "<span class=\"admin\"><span class=\"prefix\">RELAY:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins, msg)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
/datum/admins/proc/show_player_panel(mob/M in GLOB.mob_list)
set category = "Admin"
set name = "Show Player Panel"
set desc="Edit player (respawn, ban, heal, etc)"
if(!check_rights())
return
log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].")
if(!M)
to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.")
return
var/body = "<html><head><title>Options for [M.key]</title></head>"
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[M.client.ckey] || GLOB.deadmins[M.client.ckey]) ? "rank" : "add"];key=[M.key]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
if(CONFIG_GET(flag/use_exp_tracking))
body += "\[<A href='?_src_=holder;[HrefToken()];getplaytimewindow=[REF(M)]'>" + M.client.get_exp_living() + "</a>\]"
if(isnewplayer(M))
body += " <B>Hasn't Entered Game</B> "
else
body += " \[<A href='?_src_=holder;[HrefToken()];revive=[REF(M)]'>Heal</A>\] "
if(M.client)
body += "<br>\[<b>First Seen:</b> [M.client.player_join_date]\]\[<b>Byond account registered on:</b> [M.client.account_join_date]\]"
body += "<br><br><b>Show related accounts by:</b> "
body += "\[ <a href='?_src_=holder;[HrefToken()];showrelatedacc=cid;client=[REF(M.client)]'>CID</a> | "
body += "<a href='?_src_=holder;[HrefToken()];showrelatedacc=ip;client=[REF(M.client)]'>IP</a> \]"
var/rep = 0
rep += SSpersistence.antag_rep[M.ckey]
body += "<br><br>Antagonist reputation: [rep]"
body += "<br><a href='?_src_=holder;[HrefToken()];modantagrep=add;mob=[REF(M)]'>\[increase\]</a> "
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=subtract;mob=[REF(M)]'>\[decrease\]</a> "
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=set;mob=[REF(M)]'>\[set\]</a> "
body += "<a href='?_src_=holder;[HrefToken()];modantagrep=zero;mob=[REF(M)]'>\[zero\]</a>"
var/full_version = "Unknown"
if(M.client.byond_version)
full_version = "[M.client.byond_version].[M.client.byond_build ? M.client.byond_build : "xxx"]"
body += "<br>\[<b>Byond version:</b> [full_version]\]<br>"
body += "<br><br>\[ "
body += "<a href='?_src_=vars;[HrefToken()];Vars=[REF(M)]'>VV</a> - "
if(M.mind)
body += "<a href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>TP</a> - "
else
body += "<a href='?_src_=holder;[HrefToken()];initmind=[REF(M)]'>Init Mind</a> - "
if (iscyborg(M))
body += "<a href='?_src_=holder;[HrefToken()];borgpanel=[REF(M)]'>BP</a> - "
body += "<a href='?priv_msg=[M.ckey]'>PM</a> - "
body += "<a href='?_src_=holder;[HrefToken()];subtlemessage=[REF(M)]'>SM</a> - "
if (ishuman(M) && M.mind)
body += "<a href='?_src_=holder;[HrefToken()];HeadsetMessage=[REF(M)]'>HM</a> - "
body += "<a href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(M)]'>FLW</a> - "
//Default to client logs if available
var/source = LOGSRC_MOB
if(M.client)
source = LOGSRC_CLIENT
body += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_src=[source]'>LOGS</a>\] <br>"
body += "<b>Mob type</b> = [M.type]<br><br>"
body += "<A href='?_src_=holder;[HrefToken()];boot2=[REF(M)]'>Kick</A> | "
body += "<A href='?_src_=holder;[HrefToken()];newban=[REF(M)]'>Ban</A> | "
body += "<A href='?_src_=holder;[HrefToken()];jobban2=[REF(M)]'>Jobban</A> | "
body += "<A href='?_src_=holder;[HrefToken()];appearanceban=[REF(M)]'>Identity Ban</A> | "
var/rm = REF(M)
if(jobban_isbanned(M, "OOC"))
body+= "<A href='?_src_=holder;[HrefToken()];jobban3=OOC;jobban4=[rm]'><font color=red>OOCBan</font></A> | "
else
body+= "<A href='?_src_=holder;[HrefToken()];jobban3=OOC;jobban4=[rm]'>OOCBan</A> | "
if(QDELETED(M) || QDELETED(usr))
return
if(jobban_isbanned(M, "emote"))
body+= "<A href='?_src_=holder;[HrefToken()];jobban3=emote;jobban4=[rm]'><font color=red>EmoteBan</font></A> | "
else
body+= "<A href='?_src_=holder;[HrefToken()];jobban3=emote;jobban4=[rm]'>Emoteban</A> | "
if(QDELETED(M) || QDELETED(usr))
return
body += "<A href='?_src_=holder;[HrefToken()];showmessageckey=[M.ckey]'>Notes | Messages | Watchlist</A> | "
if(M.client)
body += "| <A href='?_src_=holder;[HrefToken()];sendtoprison=[REF(M)]'>Prison</A> | "
body += "\ <A href='?_src_=holder;[HrefToken()];sendbacktolobby=[REF(M)]'>Send back to Lobby</A> | "
var/muted = M.client.prefs.muted
body += "<br><b>Mute: </b> "
body += "\[<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_IC]'><font color='[(muted & MUTE_IC)?"red":"blue"]'>IC</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_OOC]'><font color='[(muted & MUTE_OOC)?"red":"blue"]'>OOC</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"blue"]'>PRAY</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"blue"]'>ADMINHELP</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a>\]"
body += "(<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"blue"]'>toggle all</font></a>)"
body += "<br><br>"
body += "<A href='?_src_=holder;[HrefToken()];jumpto=[REF(M)]'><b>Jump to</b></A> | "
body += "<A href='?_src_=holder;[HrefToken()];getmob=[REF(M)]'>Get</A> | "
body += "<A href='?_src_=holder;[HrefToken()];sendmob=[REF(M)]'>Send To</A>"
body += "<br><br>"
body += "<A href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>Traitor panel</A> | "
body += "<A href='?_src_=holder;[HrefToken()];narrateto=[REF(M)]'>Narrate to</A> | "
body += "<A href='?_src_=holder;[HrefToken()];subtlemessage=[REF(M)]'>Subtle message</A> | "
body += "<A href='?_src_=holder;[HrefToken()];languagemenu=[REF(M)]'>Language Menu</A>"
if (M.client)
if(!isnewplayer(M))
body += "<br><br>"
body += "<b>Transformation:</b>"
body += "<br>"
//Human
if(ishuman(M))
body += "<B>Human</B> | "
else
body += "<A href='?_src_=holder;[HrefToken()];humanone=[REF(M)]'>Humanize</A> | "
//Monkey
if(ismonkey(M))
body += "<B>Monkeyized</B> | "
else
body += "<A href='?_src_=holder;[HrefToken()];monkeyone=[REF(M)]'>Monkeyize</A> | "
//Corgi
if(iscorgi(M))
body += "<B>Corgized</B> | "
else
body += "<A href='?_src_=holder;[HrefToken()];corgione=[REF(M)]'>Corgize</A> | "
//AI / Cyborg
if(isAI(M))
body += "<B>Is an AI</B> "
else if(ishuman(M))
body += "<A href='?_src_=holder;[HrefToken()];makeai=[REF(M)]'>Make AI</A> | "
body += "<A href='?_src_=holder;[HrefToken()];makerobot=[REF(M)]'>Make Robot</A> | "
body += "<A href='?_src_=holder;[HrefToken()];makealien=[REF(M)]'>Make Alien</A> | "
body += "<A href='?_src_=holder;[HrefToken()];makeslime=[REF(M)]'>Make Slime</A> | "
body += "<A href='?_src_=holder;[HrefToken()];makeblob=[REF(M)]'>Make Blob</A> | "
//Simple Animals
if(isanimal(M))
body += "<A href='?_src_=holder;[HrefToken()];makeanimal=[REF(M)]'>Re-Animalize</A> | "
else
body += "<A href='?_src_=holder;[HrefToken()];makeanimal=[REF(M)]'>Animalize</A> | "
body += "<br><br>"
body += "<b>Rudimentary transformation:</b><font size=2><br>These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.</font><br>"
body += "<A href='?_src_=holder;[HrefToken()];simplemake=observer;mob=[REF(M)]'>Observer</A> | "
body += "\[ Alien: <A href='?_src_=holder;[HrefToken()];simplemake=drone;mob=[REF(M)]'>Drone</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=hunter;mob=[REF(M)]'>Hunter</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=sentinel;mob=[REF(M)]'>Sentinel</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=praetorian;mob=[REF(M)]'>Praetorian</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=queen;mob=[REF(M)]'>Queen</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=larva;mob=[REF(M)]'>Larva</A> \] "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=human;mob=[REF(M)]'>Human</A> "
body += "\[ slime: <A href='?_src_=holder;[HrefToken()];simplemake=slime;mob=[REF(M)]'>Baby</A>, "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=adultslime;mob=[REF(M)]'>Adult</A> \] "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=monkey;mob=[REF(M)]'>Monkey</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=robot;mob=[REF(M)]'>Cyborg</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=cat;mob=[REF(M)]'>Cat</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=runtime;mob=[REF(M)]'>Runtime</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=corgi;mob=[REF(M)]'>Corgi</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=ian;mob=[REF(M)]'>Ian</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=crab;mob=[REF(M)]'>Crab</A> | "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=coffee;mob=[REF(M)]'>Coffee</A> | "
body += "\[ Construct: <A href='?_src_=holder;[HrefToken()];simplemake=constructarmored;mob=[REF(M)]'>Juggernaut</A> , "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=constructbuilder;mob=[REF(M)]'>Artificer</A> , "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=constructwraith;mob=[REF(M)]'>Wraith</A> \] "
body += "<A href='?_src_=holder;[HrefToken()];simplemake=shade;mob=[REF(M)]'>Shade</A>"
body += "<br>"
if (M.client)
body += "<br><br>"
body += "<b>Other actions:</b>"
body += "<br>"
body += "<A href='?_src_=holder;[HrefToken()];forcespeech=[REF(M)]'>Forcesay</A> | "
body += "<A href='?_src_=holder;[HrefToken()];tdome1=[REF(M)]'>Thunderdome 1</A> | "
body += "<A href='?_src_=holder;[HrefToken()];tdome2=[REF(M)]'>Thunderdome 2</A> | "
body += "<A href='?_src_=holder;[HrefToken()];tdomeadmin=[REF(M)]'>Thunderdome Admin</A> | "
body += "<A href='?_src_=holder;[HrefToken()];tdomeobserve=[REF(M)]'>Thunderdome Observer</A> | "
body += usr.client.citaPPoptions(M) // CITADEL
body += "<br>"
body += "</body></html>"
usr << browse(body, "window=adminplayeropts-[REF(M)];size=550x515")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Player Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/access_news_network() //MARKER
set category = "Fun"
set name = "Access Newscaster Network"
set desc = "Allows you to view, add and edit news feeds."
if (!istype(src, /datum/admins))
src = usr.client.holder
if (!istype(src, /datum/admins))
to_chat(usr, "Error: you are not an admin!")
return
var/dat
dat = text("<HEAD><TITLE>Admin Newscaster</TITLE></HEAD><H3>Admin Newscaster Unit</H3>")
switch(admincaster_screen)
if(0)
dat += "Welcome to the admin newscaster.<BR> Here you can add, edit and censor every newspiece on the network."
dat += "<BR>Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units."
dat += "<BR>Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!</FONT>"
if(GLOB.news_network.wanted_issue.active)
dat+= "<HR><A href='?src=[REF(src)];[HrefToken()];ac_view_wanted=1'>Read Wanted Issue</A>"
dat+= "<HR><BR><A href='?src=[REF(src)];[HrefToken()];ac_create_channel=1'>Create Feed Channel</A>"
dat+= "<BR><A href='?src=[REF(src)];[HrefToken()];ac_view=1'>View Feed Channels</A>"
dat+= "<BR><A href='?src=[REF(src)];[HrefToken()];ac_create_feed_story=1'>Submit new Feed story</A>"
dat+= "<BR><BR><A href='?src=[REF(usr)];[HrefToken()];mach_close=newscaster_main'>Exit</A>"
var/wanted_already = 0
if(GLOB.news_network.wanted_issue.active)
wanted_already = 1
dat+="<HR><B>Feed Security functions:</B><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_menu_censor_story=1'>Censor Feed Stories</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_menu_censor_channel=1'>Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel).</A>"
dat+="<BR><HR><A href='?src=[REF(src)];[HrefToken()];ac_set_signature=1'>The newscaster recognises you as:<BR> <FONT COLOR='green'>[src.admin_signature]</FONT></A>"
if(1)
dat+= "Station Feed Channels<HR>"
if( isemptylist(GLOB.news_network.network_channels) )
dat+="<I>No active channels found...</I>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
if(CHANNEL.is_admin_channel)
dat+="<B><FONT style='BACKGROUND-COLOR: LightGreen'><A href='?src=[REF(src)];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A></FONT></B><BR>"
else
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR></B>"
dat+="<BR><HR><A href='?src=[REF(src)];[HrefToken()];ac_refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Back</A>"
if(2)
dat+="Creating new Feed Channel..."
dat+="<HR><B><A href='?src=[REF(src)];[HrefToken()];ac_set_channel_name=1'>Channel Name</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>"
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_set_signature=1'>Channel Author</A>:</B> <FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_set_channel_lock=1'>Will Accept Public Feeds</A>:</B> [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]<BR><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_submit_new_channel=1'>Submit</A><BR><BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Cancel</A><BR>"
if(3)
dat+="Creating new Feed Message..."
dat+="<HR><B><A href='?src=[REF(src)];[HrefToken()];ac_set_channel_receiving=1'>Receiving Channel</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>" //MARK
dat+="<B>Message Author:</B> <FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<B><A href='?src=[REF(src)];[HrefToken()];ac_set_new_message=1'>Message Body</A>:</B> [src.admincaster_feed_message.returnBody(-1)] <BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_submit_new_message=1'>Submit</A><BR><BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Cancel</A><BR>"
if(4)
dat+="Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].<BR><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
if(5)
dat+="Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.<BR><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
if(6)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed story to Network.</B></FONT><HR><BR>"
if(src.admincaster_feed_channel.channel_name=="")
dat+="<FONT COLOR='maroon'>•Invalid receiving channel name.</FONT><BR>"
if(src.admincaster_feed_message.returnBody(-1) == "" || src.admincaster_feed_message.returnBody(-1) == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid message body.</FONT><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[3]'>Return</A><BR>"
if(7)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed Channel to Network.</B></FONT><HR><BR>"
if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid channel name.</FONT><BR>"
var/check = 0
for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels)
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
check = 1
break
if(check)
dat+="<FONT COLOR='maroon'>•Channel name already in use.</FONT><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[2]'>Return</A><BR>"
if(9)
dat+="<B>[admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[created by: <FONT COLOR='maroon'>[admincaster_feed_channel.returnAuthor(-1)]</FONT>\]</FONT><HR>"
if(src.admincaster_feed_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>"
else
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
var/i = 0
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
i++
dat+="-[MESSAGE.returnBody(-1)] <BR>"
if(MESSAGE.img)
usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
dat+="<img src='tmp_photo[i].png' width = '180'><BR><BR>"
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]:<br>"
for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body]<br><font size=1>[comment.author] [comment.time_stamp]</font><br>"
dat+="<br>"
dat+="<BR><HR><A href='?src=[REF(src)];[HrefToken()];ac_refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[1]'>Back</A>"
if(10)
dat+="<B>Nanotrasen Feed Censorship Tool</B><BR>"
dat+="<FONT SIZE=1>NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<BR>"
dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.</FONT>"
dat+="<HR>Select Feed channel to get Stories from:<BR>"
if(isemptylist(GLOB.news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_censor_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Cancel</A>"
if(11)
dat+="<B>Nanotrasen D-Notice Handler</B><HR>"
dat+="<FONT SIZE=1>A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's"
dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed"
dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.</FONT><HR>"
if(isemptylist(GLOB.news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_pick_d_notice=[REF(CHANNEL)]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Back</A>"
if(12)
dat+="<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.returnAuthor(-1)]</FONT> \]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=[REF(src)];[HrefToken()];ac_censor_channel_author=[REF(src.admincaster_feed_channel)]'>[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]</A></FONT><HR>"
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=[REF(src)];[HrefToken()];ac_censor_channel_story_body=[REF(MESSAGE)]'>[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")]</A> - <A href='?src=[REF(src)];[HrefToken()];ac_censor_channel_story_author=[REF(MESSAGE)]'>[(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]</A></FONT><BR>"
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: <a href='?src=[REF(src)];[HrefToken()];ac_lock_comment=[REF(MESSAGE)]'>[MESSAGE.locked ? "Unlock" : "Lock"]</a><br>"
for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body] <a href='?src=[REF(src)];[HrefToken()];ac_del_comment=[REF(comment)];ac_del_comment_msg=[REF(MESSAGE)]'>X</a><br><font size=1>[comment.author] [comment.time_stamp]</font><br>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[10]'>Back</A>"
if(13)
dat+="<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.returnAuthor(-1)]</FONT> \]</FONT><BR>"
dat+="Channel messages listed below. If you deem them dangerous to the station, you can <A href='?src=[REF(src)];[HrefToken()];ac_toggle_d_notice=[REF(src.admincaster_feed_channel)]'>Bestow a D-Notice upon the channel</A>.<HR>"
if(src.admincaster_feed_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>"
else
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.returnAuthor(-1)]</FONT>\]</FONT><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[11]'>Back</A>"
if(14)
dat+="<B>Wanted Issue Handler:</B>"
var/wanted_already = 0
var/end_param = 1
if(GLOB.news_network.wanted_issue.active)
wanted_already = 1
end_param = 2
if(wanted_already)
dat+="<FONT SIZE=2><BR><I>A wanted issue is already in Feed Circulation. You can edit or cancel it below.</FONT></I>"
dat+="<HR>"
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_set_wanted_name=1'>Criminal Name</A>: [src.admincaster_wanted_message.criminal] <BR>"
dat+="<A href='?src=[REF(src)];[HrefToken()];ac_set_wanted_desc=1'>Description</A>: [src.admincaster_wanted_message.body] <BR>"
if(wanted_already)
dat+="<B>Wanted Issue created by:</B><FONT COLOR='green'>[GLOB.news_network.wanted_issue.scannedUser]</FONT><BR>"
else
dat+="<B>Wanted Issue will be created under prosecutor:</B><FONT COLOR='green'>[src.admin_signature]</FONT><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</A>"
if(wanted_already)
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_cancel_wanted=1'>Take down Issue</A>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Cancel</A>"
if(15)
dat+="<FONT COLOR='green'>Wanted issue for [src.admincaster_wanted_message.criminal] is now in Network Circulation.</FONT><BR><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
if(16)
dat+="<B><FONT COLOR='maroon'>ERROR: Wanted Issue rejected by Network.</B></FONT><HR><BR>"
if(src.admincaster_wanted_message.criminal =="" || src.admincaster_wanted_message.criminal == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid name for person wanted.</FONT><BR>"
if(src.admincaster_wanted_message.body == "" || src.admincaster_wanted_message.body == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid description.</FONT><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
if(17)
dat+="<B>Wanted Issue successfully deleted from Circulation</B><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
if(18)
dat+="<B><FONT COLOR ='maroon'>-- STATIONWIDE WANTED ISSUE --</B></FONT><BR><FONT SIZE=2>\[Submitted by: <FONT COLOR='green'>[GLOB.news_network.wanted_issue.scannedUser]</FONT>\]</FONT><HR>"
dat+="<B>Criminal</B>: [GLOB.news_network.wanted_issue.criminal]<BR>"
dat+="<B>Description</B>: [GLOB.news_network.wanted_issue.body]<BR>"
dat+="<B>Photo:</B>: "
if(GLOB.news_network.wanted_issue.img)
usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png")
dat+="<BR><img src='tmp_photow.png' width = '180'>"
else
dat+="None"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Back</A><BR>"
if(19)
dat+="<FONT COLOR='green'>Wanted issue for [src.admincaster_wanted_message.criminal] successfully edited.</FONT><BR><BR>"
dat+="<BR><A href='?src=[REF(src)];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
else
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
usr << browse(dat, "window=admincaster_main;size=400x600")
onclose(usr, "admincaster_main")
/datum/admins/proc/Game()
if(!check_rights(0))
return
var/dat = {"
<center><B>Game Panel</B></center><hr>\n
<A href='?src=[REF(src)];[HrefToken()];c_mode=1'>Change Game Mode</A><br>
"}
if(GLOB.master_mode == "secret")
dat += "<A href='?src=[REF(src)];[HrefToken()];f_secret=1'>(Force Secret Mode)</A><br>"
if(GLOB.master_mode == "dynamic")
if(SSticker.current_state <= GAME_STATE_PREGAME)
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
if (GLOB.dynamic_forced_roundstart_ruleset.len > 0)
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_options=1'>(Dynamic mode options)</A><br>"
else if (SSticker.IsRoundInProgress())
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if (mode.forced_latejoin_rule)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin_clear=1'>-> [mode.forced_latejoin_rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_midround=1'>(Execute Midround Ruleset!)</A><br>"
dat += "<hr/>"
if(SSticker.IsRoundInProgress())
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
dat += {"
<BR>
<A href='?src=[REF(src)];[HrefToken()];create_object=1'>Create Object</A><br>
<A href='?src=[REF(src)];[HrefToken()];quick_create_object=1'>Quick Create Object</A><br>
<A href='?src=[REF(src)];[HrefToken()];create_turf=1'>Create Turf</A><br>
<A href='?src=[REF(src)];[HrefToken()];create_mob=1'>Create Mob</A><br>
"}
if(marked_datum && istype(marked_datum, /atom))
dat += "<A href='?src=[REF(src)];[HrefToken()];dupe_marked_datum=1'>Duplicate Marked Datum</A><br>"
usr << browse(dat, "window=admin2;size=210x200")
return
/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge
//i.e. buttons/verbs
/datum/admins/proc/restart()
set category = "Server"
set name = "Reboot World"
set desc="Restarts the world immediately"
if (!usr.client.holder)
return
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
if(world.TgsAvailable())
options += "Server Restart (Kill and restart DD)";
var/rebootconfirm
if(SSticker.admin_delay_notice)
if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") == "Yes")
rebootconfirm = TRUE
else
rebootconfirm = TRUE
if(rebootconfirm)
var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
if(result)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
switch(result)
if("Regular Restart")
SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
if("Hard Restart (No Delay, No Feeback Reason)")
to_chat(world, "World reboot - [init_by]")
world.Reboot()
if("Hardest Restart (No actions, just reboot)")
to_chat(world, "Hard world reboot - [init_by]")
world.Reboot(fast_track = TRUE)
if("Server Restart (Kill and restart DD)")
to_chat(world, "Server restart - [init_by]")
world.TgsEndProcess()
/datum/admins/proc/end_round()
set category = "Server"
set name = "End Round"
set desc = "Attempts to produce a round end report and then restart the server organically."
if (!usr.client.holder)
return
var/confirm = alert("End the round and restart the game world?", "End Round", "Yes", "Cancel")
if(confirm == "Cancel")
return
if(confirm == "Yes")
SSticker.force_ending = 1
SSblackbox.record_feedback("tally", "admin_verb", 1, "End Round") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/announce()
set category = "Special Verbs"
set name = "Announce"
set desc="Announce your desires to the world"
if(!check_rights(0))
return
var/message = input("Global message to send:", "Admin Announce", null, null) as message
if(message)
if(!check_rights(R_SERVER,0))
message = adminscrub(message,500)
to_chat(world, "<span class='adminnotice'><b>[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:</b></span>\n \t [message]")
log_admin("Announce: [key_name(usr)] : [message]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/set_admin_notice()
set category = "Special Verbs"
set name = "Set Admin Notice"
set desc ="Set an announcement that appears to everyone who joins the server. Only lasts this round"
if(!check_rights(0))
return
var/new_admin_notice = input(src,"Set a public notice for this round. Everyone who joins the server will see it.\n(Leaving it blank will delete the current notice):","Set Notice",GLOB.admin_notice) as message|null
if(new_admin_notice == null)
return
if(new_admin_notice == GLOB.admin_notice)
return
if(new_admin_notice == "")
message_admins("[key_name(usr)] removed the admin notice.")
log_admin("[key_name(usr)] removed the admin notice:\n[GLOB.admin_notice]")
else
message_admins("[key_name(usr)] set the admin notice.")
log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]")
to_chat(world, "<span class ='adminnotice'><b>Admin Notice:</b>\n \t [new_admin_notice]</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
GLOB.admin_notice = new_admin_notice
return
/datum/admins/proc/toggleooc()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle OOC"
toggle_ooc()
log_admin("[key_name(usr)] toggled OOC.")
message_admins("[key_name_admin(usr)] toggled OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleooclocal()
set category = "Server"
set desc="Toggle dat bitch"
set name="Toggle Local OOC"
toggle_looc()
log_admin("[key_name(usr)] toggled LOOC.")
message_admins("[key_name_admin(usr)] toggled LOOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Local OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleoocdead()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle Dead OOC"
toggle_dooc()
log_admin("[key_name(usr)] toggled Dead OOC.")
message_admins("[key_name_admin(usr)] toggled Dead OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Dead OOC", "[GLOB.dooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleaooc()
set category = "Server"
set desc="Toggle to bitch"
set name="Toggle Antag OOC"
toggle_aooc()
log_admin("[key_name(usr)] toggled Antagonist OOC.")
message_admins("[key_name_admin(usr)] toggled Antagonist OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Antag OOC", "[GLOB.aooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/startnow()
set category = "Server"
set desc="Start the round RIGHT NOW"
set name="Start Now"
if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP)
SSticker.start_immediately = TRUE
log_admin("[usr.key] has started the game.")
var/msg = ""
if(SSticker.current_state == GAME_STATE_STARTUP)
msg = " (The server is still setting up, but the round will be \
started as soon as possible.)"
message_admins("<font color='blue'>\
[usr.key] has started the game.[msg]</font>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return 1
else
to_chat(usr, "<font color='red'>Error: Start Now: Game has already started.</font>")
return 0
/datum/admins/proc/toggleenter()
set category = "Server"
set desc="People can't enter"
set name="Toggle Entering"
GLOB.enter_allowed = !( GLOB.enter_allowed )
if (!( GLOB.enter_allowed ))
to_chat(world, "<B>New players may no longer enter the game.</B>")
else
to_chat(world, "<B>New players may now enter the game.</B>")
log_admin("[key_name(usr)] toggled new player game entering.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled new player game entering.</span>")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Entering", "[GLOB.enter_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleAI()
set category = "Server"
set desc="People can't be AI"
set name="Toggle AI"
var/alai = CONFIG_GET(flag/allow_ai)
CONFIG_SET(flag/allow_ai, !alai)
if (alai)
to_chat(world, "<B>The AI job is no longer chooseable.</B>")
else
to_chat(world, "<B>The AI job is chooseable now.</B>")
log_admin("[key_name(usr)] toggled AI allowed.")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleMulticam()
set category = "Server"
set desc="Turns mutlicam on and off."
set name="Toggle Multicam"
var/almcam = CONFIG_GET(flag/allow_ai_multicam)
CONFIG_SET(flag/allow_ai_multicam, !almcam)
if (almcam)
to_chat(world, "<B>The AI no longer has multicam.</B>")
for(var/i in GLOB.ai_list)
var/mob/living/silicon/ai/aiPlayer = i
if(aiPlayer.multicam_on)
aiPlayer.end_multicam()
else
to_chat(world, "<B>The AI now has multicam.</B>")
log_admin("[key_name(usr)] toggled AI multicam.")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Multicam", "[!almcam ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleaban()
set category = "Server"
set desc="Respawn basically"
set name="Toggle Respawn"
var/new_nores = !CONFIG_GET(flag/norespawn)
CONFIG_SET(flag/norespawn, new_nores)
if (!new_nores)
to_chat(world, "<B>You may now respawn.</B>")
else
to_chat(world, "<B>You may no longer respawn :(</B>")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"].</span>")
log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Respawn", "[!new_nores ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/delay()
set category = "Server"
set desc="Delay the game start"
set name="Delay pre-game"
var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null
if(SSticker.current_state > GAME_STATE_PREGAME)
return alert("Too late... The game has already started!")
if(newtime)
newtime = newtime*10
SSticker.SetTimeLeft(newtime)
if(newtime < 0)
to_chat(world, "<b>The game start has been delayed.</b>")
log_admin("[key_name(usr)] delayed the round start.")
else
to_chat(world, "<b>The game will start in [DisplayTimeText(newtime)].</b>")
SEND_SOUND(world, sound(get_announcer_sound("attention")))
log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggledynamicvote()
set category = "Server"
set desc="Switches between secret/extended and dynamic voting"
set name="Toggle Dynamic Vote"
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
if (!prev_dynamic_voting)
to_chat(world, "<B>Vote is now between extended and dynamic chaos.</B>")
else
to_chat(world, "<B>Vote is now between extended and secret.</B>")
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled dynamic voting.</span>")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Dynamic Voting", "[prev_dynamic_voting ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/unprison(mob/M in GLOB.mob_list)
set category = "Admin"
set name = "Unprison"
if (is_centcom_level(M.z))
SSjob.SendToLateJoin(M)
message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]")
log_admin("[key_name(usr)] has unprisoned [key_name(M)]")
else
alert("[M.name] is not prisoned.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Unprison") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS
/datum/admins/proc/spawn_atom(object as text)
set category = "Debug"
set desc = "(atom path) Spawn an atom"
set name = "Spawn"
if(!check_rights(R_SPAWN))
return
var/chosen = pick_closest_path(object)
if(!chosen)
return
if(ispath(chosen, /turf))
var/turf/T = get_turf(usr.loc)
T.ChangeTurf(chosen)
else
var/atom/A = new chosen(usr.loc)
A.flags_1 |= ADMIN_SPAWNED_1
log_admin("[key_name(usr)] spawned [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/spawn_cargo(object as text)
set category = "Debug"
set desc = "(atom path) Spawn a cargo crate"
set name = "Spawn Cargo"
if(!check_rights(R_SPAWN))
return
var/chosen = pick_closest_path(object, make_types_fancy(subtypesof(/datum/supply_pack)))
if(!chosen)
return
var/datum/supply_pack/S = new chosen
S.admin_spawned = TRUE
S.generate(get_turf(usr))
log_admin("[key_name(usr)] spawned cargo pack [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Cargo") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/show_traitor_panel(mob/M in GLOB.mob_list)
set category = "Admin"
set desc = "Edit mobs's memory and role"
set name = "Show Traitor Panel"
if(!istype(M))
to_chat(usr, "This can only be used on instances of type /mob")
return
if(!M.mind)
to_chat(usr, "This mob has no mind!")
return
M.mind.traitor_panel()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggletintedweldhelmets()
set category = "Debug"
set desc="Reduces view range when wearing welding helmets"
set name="Toggle tinted welding helmes"
GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh )
if (GLOB.tinted_weldhelh)
to_chat(world, "<B>The tinted_weldhelh has been enabled!</B>")
else
to_chat(world, "<B>The tinted_weldhelh has been disabled!</B>")
log_admin("[key_name(usr)] toggled tinted_weldhelh.")
message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Tinted Welding Helmets", "[GLOB.tinted_weldhelh ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleguests()
set category = "Server"
set desc="Guests can't enter"
set name="Toggle guests"
var/new_guest_ban = !CONFIG_GET(flag/guest_ban)
CONFIG_SET(flag/guest_ban, new_guest_ban)
if (new_guest_ban)
to_chat(world, "<B>Guests may no longer enter the game.</B>")
else
to_chat(world, "<B>Guests may now enter the game.</B>")
log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.</span>")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/output_ai_laws()
var/ai_number = 0
for(var/i in GLOB.silicon_mobs)
var/mob/living/silicon/S = i
ai_number++
if(isAI(S))
to_chat(usr, "<b>AI [key_name(S, usr)]'s laws:</b>")
else if(iscyborg(S))
var/mob/living/silicon/robot/R = S
to_chat(usr, "<b>CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:</b>")
else if (ispAI(S))
to_chat(usr, "<b>pAI [key_name(S, usr)]'s laws:</b>")
else
to_chat(usr, "<b>SOMETHING SILICON [key_name(S, usr)]'s laws:</b>")
if (S.laws == null)
to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.")
else
S.laws.show_laws(usr)
if(!ai_number)
to_chat(usr, "<b>No AIs located</b>" )
/datum/admins/proc/output_all_devil_info()
var/devil_number = 0
for(var/datum/mind/D in SSticker.mode.devils)
devil_number++
var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil)
to_chat(usr, "Devil #[devil_number]:<br><br>" + devil.printdevilinfo())
if(!devil_number)
to_chat(usr, "<b>No Devils located</b>" )
/datum/admins/proc/output_devil_info(mob/living/M)
if(is_devil(M))
var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil)
to_chat(usr, devil.printdevilinfo())
else
to_chat(usr, "<b>[M] is not a devil.")
/datum/admins/proc/manage_free_slots()
if(!check_rights())
return
var/datum/browser/browser = new(usr, "jobmanagement", "Manage Free Slots", 520)
var/list/dat = list()
var/count = 0
if(!SSjob.initialized)
alert(usr, "You cannot manage jobs before the job subsystem is initialized!")
return
dat += "<table>"
for(var/j in SSjob.occupations)
var/datum/job/job = j
count++
var/J_title = html_encode(job.title)
var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions))
var/J_totPos = html_encode(job.total_positions)
dat += "<tr><td>[J_title]:</td> <td>[J_opPos]/[job.total_positions < 0 ? " (unlimited)" : J_totPos]"
dat += "</td>"
dat += "<td>"
if(job.total_positions >= 0)
dat += "<A href='?src=[REF(src)];[HrefToken()];customjobslot=[job.title]'>Custom</A> | "
dat += "<A href='?src=[REF(src)];[HrefToken()];addjobslot=[job.title]'>Add 1</A> | "
if(job.total_positions > job.current_positions)
dat += "<A href='?src=[REF(src)];[HrefToken()];removejobslot=[job.title]'>Remove</A> | "
else
dat += "Remove | "
dat += "<A href='?src=[REF(src)];[HrefToken()];unlimitjobslot=[job.title]'>Unlimit</A></td>"
else
dat += "<A href='?src=[REF(src)];[HrefToken()];limitjobslot=[job.title]'>Limit</A></td>"
browser.height = min(100 + count * 20, 650)
browser.set_content(dat.Join())
browser.open()
/datum/admins/proc/dynamic_mode_options(mob/user)
var/dat = {"
<center><B><h2>Dynamic Mode Options</h2></B></center><hr>
<br/>
<h3>Common options</h3>
<i>All these options can be changed midround.</i> <br/>
<br/>
<b>Force extended:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_force_extended=1'> <b>[GLOB.dynamic_forced_extended ? "ON" : "OFF"]</a></b>.
<br/>This will force the round to be extended. No rulesets will be drafted. <br/>
<br/>
<b>No stacking:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_no_stacking=1'> <b>[GLOB.dynamic_no_stacking ? "ON" : "OFF"]</b></a>.
<br/>Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted. <br/>
<br/>
<b>Classic secret mode:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_classic_secret=1'> <b>[GLOB.dynamic_classic_secret ? "ON" : "OFF"]</b></a>.
<br/>Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn. <br/>
<br/>
<br/>
<b>Forced threat level:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_forced_threat=1'><b>[GLOB.dynamic_forced_threat_level]</b></a>.
<br/>The value threat is set to if it is higher than -1.<br/>
<br/>
<b>High population limit:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_high_pop_limit=1'><b>[GLOB.dynamic_high_pop_limit]</b></a>.
<br/>The threshold at which "high population override" will be in effect. <br/>
<br/>
<b>Stacking threeshold:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_stacking_limit=1'><b>[GLOB.dynamic_stacking_limit]</b></a>.
<br/>The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens. <br/>
<h3>Advanced parameters</h3>
Curve centre: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_centre=1'>-> [GLOB.dynamic_curve_centre] <-</A><br>
Curve width: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_width=1'>-> [GLOB.dynamic_curve_width] <-</A><br>
Latejoin injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_min=1'>-> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_max=1'>-> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <-</A> Minutes<br>
Midround injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_min=1'>-> [GLOB.dynamic_midround_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_max=1'>-> [GLOB.dynamic_midround_delay_max / 60 / 10] <-</A> Minutes<br>
"}
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/create_or_modify_area()
set category = "Debug"
set name = "Create or modify area"
create_area(usr)
//
//
//ALL DONE
//*********************************************************************************************************
//TO-DO:
//
//
//RIP ferry snowflakes
//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked
//defaults to kicking everyone (afk + non afk clients in the lobby)
//returns a list of ckeys of the kicked clients
/proc/kick_clients_in_lobby(message, kick_only_afk = 0)
var/list/kicked_client_names = list()
for(var/client/C in GLOB.clients)
if(isnewplayer(C.mob))
if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk
continue
if(message)
to_chat(C, message)
kicked_client_names.Add("[C.key]")
qdel(C)
return kicked_client_names
//returns 1 to let the dragdrop code know we are trapping this event
//returns 0 if we don't plan to trap the event
/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/tomob)
//this is the exact two check rights checks required to edit a ckey with vv.
if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0))
return 0
if (!frommob.ckey)
return 0
var/question = ""
if (tomob.ckey)
question = "This mob already has a user ([tomob.key]) in control of it! "
question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?"
var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No")
if (ask != "Yes")
return 1
if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response
return 1
tomob.ghostize(0)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [frommob.key] in control of [tomob.name].</span>")
log_admin("[key_name(usr)] stuffed [frommob.key] into [tomob.name].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Ghost Drag Control")
tomob.ckey = frommob.ckey
qdel(frommob)
return 1
/client/proc/adminGreet(logout)
if(SSticker.HasRoundStarted())
var/string
if(logout && CONFIG_GET(flag/announce_admin_logout))
string = pick(
"Admin logout: [key_name(src)]")
else if(!logout && CONFIG_GET(flag/announce_admin_login) && (prefs.toggles & ANNOUNCE_LOGIN))
string = pick(
"Admin login: [key_name(src)]")
if(string)
message_admins("[string]")
-719
View File
@@ -1,719 +0,0 @@
//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless
//the procs are cause you can't put the comments in the GLOB var define
GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
GLOBAL_PROTECT(admin_verbs_default)
/world/proc/AVerbsDefault()
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/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
/client/proc/toggleprayers,
/client/proc/toggleadminhelpsound
)
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
GLOBAL_PROTECT(admin_verbs_admin)
/world/proc/AVerbsAdmin()
return list(
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage
/datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/
/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*/
/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*/
/datum/admins/proc/toggleaooc, /*toggles antag ooc on/off*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/datum/admins/proc/announce, /*priority announce something to all clients.*/
/datum/admins/proc/set_admin_notice, /*announcement all clients see when joining the server.*/
/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/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
/client/proc/jumptoarea,
/client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/
/client/proc/jumptomob, /*allows us to jump to a specific mob*/
/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/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*/
/client/proc/cmd_admin_man_up, //CIT CHANGE - adds man up verb
/client/proc/cmd_admin_man_up_global, //CIT CHANGE - ditto
/client/proc/cmd_admin_create_centcom_report,
/client/proc/cmd_change_command_name,
/client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/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*/
/client/proc/open_shuttle_manipulator, /* Opens shuttle manipulator UI */
/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/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
/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/set_round_end_sound))
GLOBAL_PROTECT(admin_verbs_sounds)
GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/set_dynex_scale,
/client/proc/drop_dynex_bomb,
/client/proc/cinematic,
/client/proc/one_click_antag,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/object_say,
/client/proc/toggle_random_events,
/client/proc/set_ooc,
/client/proc/reset_ooc,
/client/proc/forceEvent,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/run_weather,
/client/proc/mass_zombie_infection,
/client/proc/mass_zombie_cure,
/client/proc/polymorph_all,
/client/proc/show_tip,
/client/proc/smite,
/client/proc/admin_away,
/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/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character))
GLOBAL_PROTECT(admin_verbs_spawn)
GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
/world/proc/AVerbsServer()
return list(
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/end_round,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/datum/admins/proc/toggleMulticam,
/datum/admins/proc/toggledynamicvote,
/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
)
GLOBAL_PROTECT(admin_verbs_server)
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/world/proc/AVerbsDebug()
return list(
/client/proc/restart_controller,
/client/proc/cmd_admin_list_open_jobs,
/client/proc/Debug2,
/client/proc/cmd_debug_make_powernets,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_admin_delete,
/client/proc/cmd_debug_del_all,
/client/proc/restart_controller,
/client/proc/enable_debug_verbs,
/client/proc/callproc,
/client/proc/callproc_datum,
/client/proc/SDQL2_query,
/client/proc/test_movable_UI,
/client/proc/test_snap_UI,
/client/proc/debugNatureMapGenerator,
/client/proc/check_bomb_impacts,
/proc/machine_upgrade,
/client/proc/populate_world,
/client/proc/get_dynex_power, //*debug verbs for dynex explosions.
/client/proc/get_dynex_range, //*debug verbs for dynex explosions.
/client/proc/set_dynex_scale,
/client/proc/cmd_display_del_log,
/client/proc/create_outfits,
/client/proc/modify_goals,
/client/proc/debug_huds,
/client/proc/map_template_load,
/client/proc/map_template_upload,
/client/proc/jump_to_ruin,
/client/proc/clear_dynamic_transit,
/client/proc/toggle_medal_disable,
/client/proc/view_runtimes,
/client/proc/pump_random_event,
/client/proc/cmd_display_init_log,
/client/proc/cmd_display_overlay_log,
/client/proc/reload_configuration,
/datum/admins/proc/create_or_modify_area,
/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))
//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/toggleenter,
/datum/admins/proc/toggleguests,
/datum/admins/proc/announce,
/datum/admins/proc/set_admin_notice,
/client/proc/admin_ghost,
/client/proc/toggle_view_range,
/client/proc/cmd_admin_subtle_message,
/client/proc/cmd_admin_headset_message,
/client/proc/cmd_admin_check_contents,
/datum/admins/proc/access_news_network,
/client/proc/admin_call_shuttle,
/client/proc/admin_cancel_shuttle,
/client/proc/cmd_admin_direct_narrate,
/client/proc/cmd_admin_world_narrate,
/client/proc/cmd_admin_local_narrate,
/client/proc/play_local_sound,
/client/proc/play_sound,
/client/proc/set_round_end_sound,
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/drop_dynex_bomb,
/client/proc/get_dynex_range,
/client/proc/get_dynex_power,
/client/proc/set_dynex_scale,
/client/proc/cinematic,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_create_centcom_report,
/client/proc/cmd_change_command_name,
/client/proc/object_say,
/client/proc/toggle_random_events,
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/client/proc/restart_controller,
/client/proc/cmd_admin_list_open_jobs,
/client/proc/callproc,
/client/proc/callproc_datum,
/client/proc/Debug2,
/client/proc/reload_admins,
/client/proc/cmd_debug_make_powernets,
/client/proc/startSinglo,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_debug_del_all,
/client/proc/enable_debug_verbs,
/proc/possess,
/proc/release,
/client/proc/reload_admins,
/client/proc/panicbunker,
/client/proc/addbunkerbypass,
/client/proc/revokebunkerbypass,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/cmd_display_del_log,
/client/proc/toggle_combo_hud,
/client/proc/debug_huds,
/client/proc/cmd_admin_man_up, //CIT CHANGE - adds man up verb
/client/proc/cmd_admin_man_up_global //CIT CHANGE - ditto
))
GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/add_admin_verbs()
if(holder)
control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
var/rights = holder.rank.rights
verbs += GLOB.admin_verbs_default
if(rights & R_BUILDMODE)
verbs += /client/proc/togglebuildmodeself
if(rights & R_ADMIN)
verbs += GLOB.admin_verbs_admin
if(rights & R_BAN)
verbs += GLOB.admin_verbs_ban
if(rights & R_FUN)
verbs += GLOB.admin_verbs_fun
if(rights & R_SERVER)
verbs += GLOB.admin_verbs_server
if(rights & R_DEBUG)
verbs += GLOB.admin_verbs_debug
if(rights & R_POSSESS)
verbs += GLOB.admin_verbs_possess
if(rights & R_PERMISSIONS)
verbs += GLOB.admin_verbs_permissions
if(rights & R_STEALTH)
verbs += /client/proc/stealth
if(rights & R_ADMIN)
verbs += GLOB.admin_verbs_poll
if(rights & R_SOUNDS)
verbs += GLOB.admin_verbs_sounds
if(CONFIG_GET(string/invoke_youtubedl))
verbs += /client/proc/play_web_sound
if(rights & R_SPAWN)
verbs += GLOB.admin_verbs_spawn
/client/proc/remove_admin_verbs()
verbs.Remove(
GLOB.admin_verbs_default,
/client/proc/togglebuildmodeself,
GLOB.admin_verbs_admin,
GLOB.admin_verbs_ban,
GLOB.admin_verbs_fun,
GLOB.admin_verbs_server,
GLOB.admin_verbs_debug,
GLOB.admin_verbs_possess,
GLOB.admin_verbs_permissions,
/client/proc/stealth,
GLOB.admin_verbs_poll,
GLOB.admin_verbs_sounds,
/client/proc/play_web_sound,
GLOB.admin_verbs_spawn,
/*Debug verbs added by "show debug verbs"*/
GLOB.admin_verbs_debug_mapping,
/client/proc/disable_debug_verbs,
/client/proc/readmin
)
/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs
set name = "Adminverbs - Hide Most"
set category = "Admin"
verbs.Remove(/client/proc/hide_most_verbs, GLOB.admin_verbs_hideable)
verbs += /client/proc/show_verbs
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>")
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
/client/proc/hide_verbs()
set name = "Adminverbs - Hide All"
set category = "Admin"
remove_admin_verbs()
verbs += /client/proc/show_verbs
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>")
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
/client/proc/show_verbs()
set name = "Adminverbs - Show"
set category = "Admin"
verbs -= /client/proc/show_verbs
add_admin_verbs()
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>")
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!
/client/proc/admin_ghost()
set category = "Admin"
set name = "Aghost"
if(!holder)
return
if(isobserver(mob))
//re-enter
var/mob/dead/observer/ghost = mob
if(!ghost.mind || !ghost.mind.current) //won't do anything if there is no body
return
if(!ghost.can_reenter_corpse)
log_admin("[key_name(usr)] re-entered corpse")
message_admins("[key_name_admin(usr)] re-entered corpse")
ghost.can_reenter_corpse = 1 //force re-entering even when otherwise not possible
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>")
else
//ghostize
log_admin("[key_name(usr)] admin ghosted.")
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1)
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!
/client/proc/invisimin()
set name = "Invisimin"
set category = "Admin"
set desc = "Toggles ghost-like invisibility (Don't abuse this)"
if(holder && mob)
if(mob.invisibility == INVISIBILITY_OBSERVER)
mob.invisibility = initial(mob.invisibility)
to_chat(mob, "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>")
else
mob.invisibility = INVISIBILITY_OBSERVER
to_chat(mob, "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>")
/client/proc/check_antagonists()
set name = "Check Antagonists"
set category = "Admin"
if(holder)
holder.check_antagonists()
log_admin("[key_name(usr)] checked antagonists.") //for tsar~
if(!isobserver(usr) && SSticker.HasRoundStarted())
message_admins("[key_name_admin(usr)] checked antagonists.")
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 category = "Admin"
if(holder)
if(CONFIG_GET(flag/ban_legacy_system))
holder.unbanpanel()
else
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/game_panel()
set name = "Game Panel"
set category = "Admin"
if(holder)
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"
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/findStealthKey(txt)
if(txt)
for(var/P in GLOB.stealthminID)
if(GLOB.stealthminID[P] == txt)
return P
txt = GLOB.stealthminID[ckey]
return txt
/client/proc/createStealthKey()
var/num = (rand(0,1000))
var/i = 0
while(i == 0)
i = 1
for(var/P in GLOB.stealthminID)
if(num == GLOB.stealthminID[P])
num++
i = 0
GLOB.stealthminID["[ckey]"] = "@[num2text(num)]"
/client/proc/stealth()
set category = "Admin"
set name = "Stealth Mode"
if(holder)
if(holder.fakekey)
holder.fakekey = null
if(isobserver(mob))
mob.invisibility = initial(mob.invisibility)
mob.alpha = initial(mob.alpha)
mob.name = initial(mob.name)
mob.mouse_opacity = initial(mob.mouse_opacity)
else
var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null)
if(!new_key)
return
if(length(new_key) >= 26)
new_key = copytext(new_key, 1, 26)
holder.fakekey = new_key
createStealthKey()
if(isobserver(mob))
mob.invisibility = INVISIBILITY_MAXIMUM //JUST IN CASE
mob.alpha = 0 //JUUUUST IN CASE
mob.name = " "
mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]")
message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stealth Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/drop_bomb()
set category = "Special Verbs"
set name = "Drop Bomb"
set desc = "Cause an explosion of varying strength at your location."
var/list/choices = list("Small Bomb (1, 2, 3, 3)", "Medium Bomb (2, 3, 4, 4)", "Big Bomb (3, 5, 7, 5)", "Maxcap", "Custom Bomb")
var/choice = input("What size explosion would you like to produce? NOTE: You can do all this rapidly and in an IC manner (using cruise missiles!) with the Config/Launch Supplypod verb. WARNING: These ignore the maxcap") as null|anything in choices
var/turf/epicenter = mob.loc
switch(choice)
if(null)
return 0
if("Small Bomb (1, 2, 3, 3)")
explosion(epicenter, 1, 2, 3, 3, TRUE, TRUE)
if("Medium Bomb (2, 3, 4, 4)")
explosion(epicenter, 2, 3, 4, 4, TRUE, TRUE)
if("Big Bomb (3, 5, 7, 5)")
explosion(epicenter, 3, 5, 7, 5, TRUE, TRUE)
if("Maxcap")
explosion(epicenter, GLOB.MAX_EX_DEVESTATION_RANGE, GLOB.MAX_EX_HEAVY_RANGE, GLOB.MAX_EX_LIGHT_RANGE, GLOB.MAX_EX_FLASH_RANGE)
if("Custom Bomb")
var/devastation_range = input("Devastation range (in tiles):") as null|num
if(devastation_range == null)
return
var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num
if(heavy_impact_range == null)
return
var/light_impact_range = input("Light impact range (in tiles):") as null|num
if(light_impact_range == null)
return
var/flash_range = input("Flash range (in tiles):") as null|num
if(flash_range == null)
return
if(devastation_range > GLOB.MAX_EX_DEVESTATION_RANGE || heavy_impact_range > GLOB.MAX_EX_HEAVY_RANGE || light_impact_range > GLOB.MAX_EX_LIGHT_RANGE || flash_range > GLOB.MAX_EX_FLASH_RANGE)
if(alert("Bomb is bigger than the maxcap. Continue?",,"Yes","No") != "Yes")
return
epicenter = mob.loc //We need to reupdate as they may have moved again
explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, TRUE, TRUE)
message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/drop_dynex_bomb()
set category = "Special Verbs"
set name = "Drop DynEx Bomb"
set desc = "Cause an explosion of varying strength at your location."
var/ex_power = input("Explosive Power:") as null|num
var/turf/epicenter = mob.loc
if(ex_power && epicenter)
dyn_explosion(epicenter, ex_power)
message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Dynamic Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/get_dynex_range()
set category = "Debug"
set name = "Get DynEx Range"
set desc = "Get the estimated range of a bomb, using explosive power."
var/ex_power = input("Explosive Power:") as null|num
if (isnull(ex_power))
return
var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])")
/client/proc/get_dynex_power()
set category = "Debug"
set name = "Get DynEx Power"
set desc = "Get the estimated required power of a bomb, to reach a specific range."
var/ex_range = input("Light Explosion Range:") as null|num
if (isnull(ex_range))
return
var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Power: [power]")
/client/proc/set_dynex_scale()
set category = "Debug"
set name = "Set DynEx Scale"
set desc = "Set the scale multiplier of dynex explosions. The default is 0.5."
var/ex_scale = input("New DynEx Scale:") as null|num
if(!ex_scale)
return
GLOB.DYN_EX_SCALE = ex_scale
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/give_spell(mob/T in GLOB.mob_list)
set category = "Fun"
set name = "Give Spell"
set desc = "Gives a spell to a mob."
var/list/spell_list = list()
var/type_length = length("/obj/effect/proc_holder/spell") + 2
for(var/A in GLOB.spells)
spell_list[copytext("[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
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>")
S = spell_list[S]
if(T.mind)
T.mind.AddSpell(new S)
else
T.AddSpell(new S)
message_admins("<span class='danger'>Spells given to mindless mobs will not be transferred in mindswap or cloning!</span>")
/client/proc/remove_spell(mob/T in GLOB.mob_list)
set category = "Fun"
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(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>")
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)
set category = "Fun"
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>")
return
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in SSdisease.diseases
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>")
/client/proc/object_say(obj/O in world)
set category = "Special Verbs"
set name = "OSay"
set desc = "Makes an object say something."
var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null
if(!message)
return
O.say(message)
log_admin("[key_name(usr)] made [O] at [AREACOORD(O)] say \"[message]\"")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made [O] at [AREACOORD(O)]. say \"[message]\"</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Object Say") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/togglebuildmodeself()
set name = "Toggle Build Mode Self"
set category = "Special Verbs"
if (!(holder.rank.rights & R_BUILDMODE))
return
if(src.mob)
togglebuildmode(src.mob)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Build Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/check_ai_laws()
set name = "Check AI Laws"
set category = "Admin"
if(holder)
src.holder.output_ai_laws()
/client/proc/deadmin()
set name = "Deadmin"
set category = "Admin"
set desc = "Shed your admin powers."
if(!holder)
return
if(has_antag_hud())
toggle_combo_hud()
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.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin")
/client/proc/readmin()
set name = "Readmin"
set category = "Admin"
set desc = "Regain your admin powers."
var/datum/admins/A = GLOB.deadmins[ckey]
if(!A)
A = GLOB.admin_datums[ckey]
if (!A)
var/msg = " is trying to readmin but they have no deadmin entry"
message_admins("[key_name_admin(src)][msg]")
log_admin_private("[key_name(src)][msg]")
return
A.associate(src)
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>")
message_admins("[src] re-adminned themselves.")
log_admin("[src] re-adminned themselves.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin")
/client/proc/populate_world(amount = 50 as num)
set name = "Populate World"
set category = "Debug"
set desc = "(\"Amount of mobs to create\") Populate the world with test mobs."
if (amount > 0)
var/area/area
var/list/candidates
var/turf/open/floor/tile
var/j,k
for (var/i = 1 to amount)
j = 100
do
area = pick(GLOB.the_station_areas)
if (area)
candidates = get_area_turfs(area)
if (candidates.len)
k = 100
do
tile = pick(candidates)
while ((!tile || !istype(tile)) && --k > 0)
if (tile)
var/mob/living/carbon/human/hooman = new(tile)
hooman.equipOutfit(pick(subtypesof(/datum/outfit)))
testing("Spawned test mob at [COORD(tile)]")
while (!area && --j > 0)
/client/proc/toggle_AI_interact()
set name = "Toggle Admin AI Interact"
set category = "Admin"
set desc = "Allows you to interact with most machines as an AI would as a ghost"
AI_Interact = !AI_Interact
if(mob && IsAdminGhost(mob))
mob.has_unlimited_silicon_privilege = AI_Interact
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
-46
View File
@@ -1,46 +0,0 @@
/datum/admins/proc/create_mob(mob/user)
var/static/create_mob_html
if (!create_mob_html)
var/mobjs = null
mobjs = jointext(typesof(/mob), ";")
create_mob_html = file2text('html/create_object.html')
create_mob_html = replacetext(create_mob_html, "Create Object", "Create Mob")
create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"")
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475")
/proc/randomize_human(mob/living/carbon/human/H)
H.gender = pick(MALE, FEMALE)
H.real_name = random_unique_name(H.gender)
H.name = H.real_name
H.underwear = random_underwear(H.gender)
H.undie_color = random_short_color()
H.undershirt = random_undershirt(H.gender)
H.shirt_color = random_short_color()
H.skin_tone = random_skin_tone()
H.hair_style = random_hair_style(H.gender)
H.facial_hair_style = random_facial_hair_style(H.gender)
H.hair_color = random_short_color()
H.facial_hair_color = H.hair_color
H.eye_color = random_eye_color()
H.dna.blood_type = random_blood_type()
H.saved_underwear = H.underwear
H.saved_undershirt = H.undershirt
H.saved_socks = H.socks
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
H.dna.features["mcolor"] = random_short_color()
H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
H.dna.features["snout"] = pick(GLOB.snouts_list)
H.dna.features["horns"] = pick(GLOB.horns_list)
H.dna.features["frills"] = pick(GLOB.frills_list)
H.dna.features["spines"] = pick(GLOB.spines_list)
H.dna.features["body_markings"] = pick(GLOB.body_markings_list)
H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list)
H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list)
H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list)
H.update_body()
H.update_hair()
H.update_body_parts()
-754
View File
@@ -1,754 +0,0 @@
/datum/admins/proc/Secrets()
if(!check_rights(0))
return
var/list/dat = list("<B>The first rule of adminbuse is: you don't talk about the adminbuse.</B><HR>")
dat +={"
<B>General Secrets</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=admin_log'>Admin Log</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=show_admins'>Show Admin List</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=mentor_log'>Mentor Log</A><BR>
<BR>
"}
if(check_rights(R_ADMIN,0))
dat += {"
<B>Admin Secrets</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=clear_virus'>Cure all diseases currently in existence</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_bombers'>Bombing List</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=check_antagonist'>Show current traitors and objectives</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_signalers'>Show last [length(GLOB.lastsignalers)] signalers</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_lawchanges'>Show last [length(GLOB.lawchanges)] law changes</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=showailaws'>Show AI Laws</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=showgm'>Show Game Mode</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=manifest'>Show Crew Manifest</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=DNA'>List DNA (Blood)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=fingerprints'>List Fingerprints</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=ctfbutton'>Enable/Disable CTF</A><BR><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=tdomereset'>Reset Thunderdome to default state</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=set_name'>Rename Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=reset_name'>Reset Station Name</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=night_shift_set'>Set Night Shift Mode</A><BR>
<BR>
<B>Shuttles</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=moveferry'>Move Ferry</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=togglearrivals'>Toggle Arrivals Ferry</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=moveminingshuttle'>Move Mining Shuttle</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=movelaborshuttle'>Move Labor Shuttle</A><BR>
<BR>
"}
if(check_rights(R_FUN,0))
dat += {"
<B>Fun Secrets</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=virus'>Trigger a Virus Outbreak</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=monkey'>Turn all humans into monkeys</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=anime'>Chinese Cartoons</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=allspecies'>Change the species of all humans</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=power'>Make all areas powered</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=unpower'>Make all areas unpowered</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=quickpower'>Power all SMES</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=tripleAI'>Triple AI mode (needs to be used in the lobby)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=traitor_all'>Everyone is the traitor</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=ak47s'>AK-47s For Everyone!</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=guns'>Summon Guns</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=magic'>Summon Magic</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=events'>Summon Events (Toggle)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=onlyone'>There can only be one!</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=delayed_onlyone'>There can only be one! (40-second delay)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=retardify'>Make all players retarded</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=eagles'>Egalitarian Station Mode</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=blackout'>Break all lights</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=whiteout'>Fix all lights</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=floorlava'>The floor is lava! (DANGEROUS: extremely lame)</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=customportal'>Spawn a custom portal storm</A><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=flipmovement'>Flip client movement directions</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=randommovement'>Randomize client movement directions</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=custommovement'>Set each movement direction manually</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=resetmovement'>Reset movement directions to default</A><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=changebombcap'>Change bomb cap</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=masspurrbation'>Mass Purrbation</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=massremovepurrbation'>Mass Remove Purrbation</A><BR>
"}
dat += "<BR>"
if(check_rights(R_DEBUG,0))
dat += {"
<B>Security Level Elevated</B><BR>
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=maint_access_engiebrig'>Change all maintenance doors to engie/brig access only</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=maint_access_brig'>Change all maintenance doors to brig access only</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=infinite_sec'>Remove cap on security officers</A><BR>
<BR>
"}
usr << browse(dat.Join(), "window=secrets")
return
/datum/admins/proc/Secrets_topic(item,href_list)
var/datum/round_event/E
var/ok = 0
switch(item)
if("admin_log")
var/dat = "<B>Admin Log<HR></B>"
for(var/l in GLOB.admin_log)
dat += "<li>[l]</li>"
if(!GLOB.admin_log.len)
dat += "No-one has done anything this round!"
usr << browse(dat, "window=admin_log")
if("mentor_log")
CitadelMentorLogSecret()
if("show_admins")
var/dat = "<B>Current admins:</B><HR>"
if(GLOB.admin_datums)
for(var/ckey in GLOB.admin_datums)
var/datum/admins/D = GLOB.admin_datums[ckey]
dat += "[ckey] - [D.rank.name]<br>"
usr << browse(dat, "window=showadmins;size=600x500")
if("tdomereset")
if(!check_rights(R_ADMIN))
return
var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel")
if(delete_mobs == "Cancel")
return
log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].</span>")
var/area/thunderdome = locate(/area/tdome/arena)
if(delete_mobs == "Yes")
for(var/mob/living/mob in thunderdome)
qdel(mob) //Clear mobs
for(var/obj/obj in thunderdome)
if(!istype(obj, /obj/machinery/camera))
qdel(obj) //Clear objects
var/area/template = locate(/area/tdome/arena_source)
template.copy_contents_to(thunderdome)
if("clear_virus")
var/choice = input("Are you sure you want to cure all disease?") in list("Yes", "Cancel")
if(choice == "Yes")
message_admins("[key_name_admin(usr)] has cured all diseases.")
for(var/thing in SSdisease.active_diseases)
var/datum/disease/D = thing
D.cure(0)
if("set_name")
if(!check_rights(R_ADMIN))
return
var/new_name = input(usr, "Please input a new name for the station.", "What?", "") as text|null
if(!new_name)
return
set_station_name(new_name)
log_admin("[key_name(usr)] renamed the station to \"[new_name]\".")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] renamed the station to: [new_name].</span>")
priority_announce("[command_name()] has renamed the station to \"[new_name]\".")
if("night_shift_set")
if(!check_rights(R_ADMIN))
return
var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic")
switch(val)
if("Automatic")
if(CONFIG_GET(flag/enable_night_shifts))
SSnightshift.can_fire = TRUE
SSnightshift.fire()
else
SSnightshift.update_nightshift(FALSE, TRUE)
if("On")
SSnightshift.can_fire = FALSE
SSnightshift.update_nightshift(TRUE, TRUE)
if("Off")
SSnightshift.can_fire = FALSE
SSnightshift.update_nightshift(FALSE, TRUE)
if("reset_name")
if(!check_rights(R_ADMIN))
return
var/new_name = new_station_name()
set_station_name(new_name)
log_admin("[key_name(usr)] reset the station name.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] reset the station name.</span>")
priority_announce("[command_name()] has renamed the station to \"[new_name]\".")
if("list_bombers")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Bombing List</B><HR>"
for(var/l in GLOB.bombers)
dat += text("[l]<BR>")
usr << browse(dat, "window=bombers")
if("list_signalers")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing last [length(GLOB.lastsignalers)] signalers.</B><HR>"
for(var/sig in GLOB.lastsignalers)
dat += "[sig]<BR>"
usr << browse(dat, "window=lastsignalers;size=800x500")
if("list_lawchanges")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing last [length(GLOB.lawchanges)] law changes.</B><HR>"
for(var/sig in GLOB.lawchanges)
dat += "[sig]<BR>"
usr << browse(dat, "window=lawchanges;size=800x500")
if("moveminingshuttle")
if(!check_rights(R_ADMIN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Mining Shuttle"))
if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away"))
message_admins("[key_name_admin(usr)] moved mining shuttle")
log_admin("[key_name(usr)] moved the mining shuttle")
if("movelaborshuttle")
if(!check_rights(R_ADMIN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Labor Shuttle"))
if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away"))
message_admins("[key_name_admin(usr)] moved labor shuttle")
log_admin("[key_name(usr)] moved the labor shuttle")
if("moveferry")
if(!check_rights(R_ADMIN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send CentCom Ferry"))
if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away"))
message_admins("[key_name_admin(usr)] moved the CentCom ferry")
log_admin("[key_name(usr)] moved the CentCom ferry")
if("togglearrivals")
if(!check_rights(R_ADMIN))
return
var/obj/docking_port/mobile/arrivals/A = SSshuttle.arrivals
if(A)
var/new_perma = !A.perma_docked
A.perma_docked = new_perma
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Permadock Arrivals Shuttle", "[new_perma ? "Enabled" : "Disabled"]"))
message_admins("[key_name_admin(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle")
log_admin("[key_name(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle")
else
to_chat(usr, "<span class='admin'>There is no arrivals shuttle</span>")
if("showailaws")
if(!check_rights(R_ADMIN))
return
output_ai_laws()
if("showgm")
if(!check_rights(R_ADMIN))
return
if(!SSticker.HasRoundStarted())
alert("The game hasn't started yet!")
else if (SSticker.mode)
alert("The game mode is [SSticker.mode.name]")
else alert("For some reason there's a SSticker, but not a game mode")
if("manifest")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing Crew Manifest.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>Position</th></tr>"
for(var/datum/data/record/t in GLOB.data_core.general)
dat += "<tr><td>[t.fields["name"]]</td><td>[t.fields["rank"]]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=manifest;size=440x410")
if("DNA")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing DNA from blood.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>DNA</th><th>Blood Type</th></tr>"
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
if(H.ckey)
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna.blood_type]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=DNA;size=440x410")
if("fingerprints")
if(!check_rights(R_ADMIN))
return
var/dat = "<B>Showing Fingerprints.</B><HR>"
dat += "<table cellspacing=5><tr><th>Name</th><th>Fingerprints</th></tr>"
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
if(H.ckey)
dat += "<tr><td>[H]</td><td>[md5(H.dna.uni_identity)]</td></tr>"
dat += "</table>"
usr << browse(dat, "window=fingerprints;size=440x410")
if("monkey")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Monkeyize All Humans"))
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
spawn(0)
H.monkeyize()
ok = 1
if("allspecies")
if(!check_rights(R_FUN))
return
var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
if(result)
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Species Change", "[result]"))
log_admin("[key_name(usr)] turned all humans into [result]", 1)
message_admins("\blue [key_name_admin(usr)] turned all humans into [result]")
var/newtype = GLOB.species_list[result]
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
H.set_species(newtype)
if("tripleAI")
if(!check_rights(R_FUN))
return
usr.client.triple_ai()
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Triple AI"))
if("power")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All APCs"))
log_admin("[key_name(usr)] made all areas powered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all areas powered</span>")
power_restore()
if("unpower")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Depower All APCs"))
log_admin("[key_name(usr)] made all areas unpowered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all areas unpowered</span>")
power_failure()
if("quickpower")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All SMESs"))
log_admin("[key_name(usr)] made all SMESs powered", 1)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] made all SMESs powered</span>")
power_restore_quick()
if("traitor_all")
if(!check_rights(R_FUN))
return
if(!SSticker.HasRoundStarted())
alert("The game hasn't started yet!")
return
var/objective = copytext(sanitize(input("Enter an objective")),1,MAX_MESSAGE_LEN)
if(!objective)
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]"))
for(var/mob/living/H in GLOB.player_list)
if(!(ishuman(H)||istype(H, /mob/living/silicon/)))
continue
if(H.stat == DEAD || !H.client || !H.mind || ispAI(H))
continue
if(is_special_character(H))
continue
var/datum/antagonist/traitor/T = new()
T.give_objectives = FALSE
var/datum/objective/new_objective = new
new_objective.owner = H
new_objective.explanation_text = objective
T.add_objective(new_objective)
H.mind.add_antag_datum(T)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]</span>")
log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]")
if("changebombcap")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Bomb Cap"))
var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE) as num|null
if (!CONFIG_SET(number/bombcap, newBombCap))
return
message_admins("<span class='boldannounce'>[key_name_admin(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]</span>")
log_admin("[key_name(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]")
if("blackout")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Break All Lights"))
message_admins("[key_name_admin(usr)] broke all lights")
for(var/obj/machinery/light/L in GLOB.machines)
L.break_light_tube()
if("anime")
if(!check_rights(R_FUN))
return
var/animetype = alert("Would you like to have the clothes be changed?",,"Yes","No","Cancel")
var/droptype
if(animetype =="Yes")
droptype = alert("Make the uniforms Nodrop?",,"Yes","No","Cancel")
if(animetype == "Cancel" || droptype == "Cancel")
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Chinese Cartoons"))
message_admins("[key_name_admin(usr)] made everything kawaii.")
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
SEND_SOUND(H, sound(get_announcer_sound("animes")))
if(H.dna.species.id == "human")
if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
var/obj/item/organ/ears/cat/ears = new
var/obj/item/organ/tail/cat/tail = new
ears.Insert(H, drop_if_replaced=FALSE)
tail.Insert(H, drop_if_replaced=FALSE)
var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san")) //John Robust -> Robust-kun
var/list/names = splittext(H.real_name," ")
var/forename = names.len > 1 ? names[2] : names[1]
var/newname = "[forename]-[pick(honorifics["[H.gender]"])]"
H.fully_replace_character_name(H.real_name,newname)
H.update_mutant_bodyparts()
if(animetype == "Yes")
var/seifuku = pick(typesof(/obj/item/clothing/under/schoolgirl))
var/obj/item/clothing/under/schoolgirl/I = new seifuku
var/olduniform = H.w_uniform
H.temporarilyRemoveItemFromInventory(H.w_uniform, TRUE, FALSE)
H.equip_to_slot_or_del(I, SLOT_W_UNIFORM)
qdel(olduniform)
if(droptype == "Yes")
ADD_TRAIT(I, TRAIT_NODROP, ADMIN_TRAIT)
else
to_chat(H, "You're not kawaii enough for this.")
if("whiteout")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Fix All Lights"))
message_admins("[key_name_admin(usr)] fixed all lights")
for(var/obj/machinery/light/L in GLOB.machines)
L.fix()
if("floorlava")
SSweather.run_weather(/datum/weather/floor_is_lava)
if("virus")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Virus Outbreak"))
switch(alert("Do you want this to be a random disease or do you have something in mind?",,"Make Your Own","Random","Choose"))
if("Make Your Own")
AdminCreateVirus(usr.client)
if("Random")
E = new /datum/round_event/disease_outbreak()
if("Choose")
var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in typesof(/datum/disease)
E = new /datum/round_event/disease_outbreak{}()
var/datum/round_event/disease_outbreak/DO = E
DO.virus_type = virus
if("retardify")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage"))
for(var/mob/living/carbon/human/H in GLOB.player_list)
to_chat(H, "<span class='boldannounce'>You suddenly feel stupid.</span>")
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80)
message_admins("[key_name_admin(usr)] made everybody retarded")
if("eagles")//SCRAW
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Egalitarian Station"))
for(var/obj/machinery/door/airlock/W in GLOB.machines)
if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison))
W.req_access = list()
message_admins("[key_name_admin(usr)] activated Egalitarian Station mode")
priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, "commandreport")
if("ak47s")
if(!check_rights(R_FUN))
return
message_admins("[key_name_admin(usr)] activated AK-47s for Everyone!")
usr.client.ak47s()
sound_to_playing_players('sound/misc/ak47s.ogg')
if("guns")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Guns"))
var/survivor_probability = 0
switch(alert("Do you want this to create survivors antagonists?",,"No Antags","Some Antags","All Antags!"))
if("Some Antags")
survivor_probability = 25
if("All Antags!")
survivor_probability = 100
rightandwrong(SUMMON_GUNS, usr, survivor_probability)
if("magic")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Magic"))
var/survivor_probability = 0
switch(alert("Do you want this to create survivors antagonists?",,"No Antags","Some Antags","All Antags!"))
if("Some Antags")
survivor_probability = 25
if("All Antags!")
survivor_probability = 100
rightandwrong(SUMMON_MAGIC, usr, survivor_probability)
if("events")
if(!check_rights(R_FUN))
return
if(!SSevents.wizardmode)
if(alert("Do you want to toggle summon events on?",,"Yes","No") == "Yes")
summonevents()
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Events", "Activate"))
else
switch(alert("What would you like to do?",,"Intensify Summon Events","Turn Off Summon Events","Nothing"))
if("Intensify Summon Events")
summonevents()
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Events", "Intensify"))
if("Turn Off Summon Events")
SSevents.toggleWizardmode()
SSevents.resetFrequency()
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Events", "Disable"))
if("dorf")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Dwarf Beards"))
for(var/mob/living/carbon/human/B in GLOB.carbon_list)
B.facial_hair_style = "Dward Beard"
B.update_hair()
message_admins("[key_name_admin(usr)] activated dorf mode")
if("onlyone")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("There Can Be Only One"))
usr.client.only_one()
sound_to_playing_players('sound/misc/highlander.ogg')
if("delayed_onlyone")
if(!check_rights(R_FUN))
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("There Can Be Only One"))
usr.client.only_one_delayed()
sound_to_playing_players('sound/misc/highlander_delayed.ogg')
if("maint_access_brig")
if(!check_rights(R_DEBUG))
return
for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines)
M.check_access()
if (ACCESS_MAINT_TUNNELS in M.req_access)
M.req_access = list(ACCESS_BRIG)
message_admins("[key_name_admin(usr)] made all maint doors brig access-only.")
if("maint_access_engiebrig")
if(!check_rights(R_DEBUG))
return
for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines)
M.check_access()
if (ACCESS_MAINT_TUNNELS in M.req_access)
M.req_access = list()
M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE)
message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.")
if("infinite_sec")
if(!check_rights(R_DEBUG))
return
var/datum/job/J = SSjob.GetJob("Security Officer")
if(!J)
return
J.total_positions = -1
J.spawn_positions = -1
message_admins("[key_name_admin(usr)] has removed the cap on security officers.")
if("ctfbutton")
if(!check_rights(R_ADMIN))
return
toggle_all_ctf(usr)
if("masspurrbation")
if(!check_rights(R_FUN))
return
mass_purrbation()
message_admins("[key_name_admin(usr)] has put everyone on \
purrbation!")
log_admin("[key_name(usr)] has put everyone on purrbation.")
if("massremovepurrbation")
if(!check_rights(R_FUN))
return
mass_remove_purrbation()
message_admins("[key_name_admin(usr)] has removed everyone from \
purrbation.")
log_admin("[key_name(usr)] has removed everyone from purrbation.")
if("flipmovement")
if(!check_rights(R_FUN))
return
if(alert("Flip all movement controls?","Confirm","Yes","Cancel") == "Cancel")
return
var/list/movement_keys = SSinput.movement_keys
for(var/i in 1 to movement_keys.len)
var/key = movement_keys[i]
movement_keys[key] = turn(movement_keys[key], 180)
message_admins("[key_name_admin(usr)] has flipped all movement directions.")
log_admin("[key_name(usr)] has flipped all movement directions.")
if("randommovement")
if(!check_rights(R_FUN))
return
if(alert("Randomize all movement controls?","Confirm","Yes","Cancel") == "Cancel")
return
var/list/movement_keys = SSinput.movement_keys
for(var/i in 1 to movement_keys.len)
var/key = movement_keys[i]
movement_keys[key] = turn(movement_keys[key], 45 * rand(1, 8))
message_admins("[key_name_admin(usr)] has randomized all movement directions.")
log_admin("[key_name(usr)] has randomized all movement directions.")
if("custommovement")
if(!check_rights(R_FUN))
return
if(alert("Are you sure you want to change every movement key?","Confirm","Yes","Cancel") == "Cancel")
return
var/list/movement_keys = SSinput.movement_keys
var/list/new_movement = list()
for(var/i in 1 to movement_keys.len)
var/key = movement_keys[i]
var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast"
var/title = "New direction for [key]"
var/new_direction = text2dir(input(usr, msg, title) as text|null)
if(!new_direction)
new_direction = movement_keys[key]
new_movement[key] = new_direction
SSinput.movement_keys = new_movement
message_admins("[key_name_admin(usr)] has configured all movement directions.")
log_admin("[key_name(usr)] has configured all movement directions.")
if("resetmovement")
if(!check_rights(R_FUN))
return
if(alert("Are you sure you want to reset movement keys to default?","Confirm","Yes","Cancel") == "Cancel")
return
SSinput.setup_default_movement_keys()
message_admins("[key_name_admin(usr)] has reset all movement keys.")
log_admin("[key_name(usr)] has reset all movement keys.")
if("customportal")
if(!check_rights(R_FUN))
return
var/list/settings = list(
"mainsettings" = list(
"typepath" = list("desc" = "Path to spawn", "type" = "datum", "path" = "/mob/living", "subtypesonly" = TRUE, "value" = /mob/living/simple_animal/hostile/poison/bees),
"humanoutfit" = list("desc" = "Outfit if human", "type" = "datum", "path" = "/datum/outfit", "subtypesonly" = TRUE, "value" = /datum/outfit),
"amount" = list("desc" = "Number per portal", "type" = "number", "value" = 1),
"portalnum" = list("desc" = "Number of total portals", "type" = "number", "value" = 10),
"offerghosts" = list("desc" = "Get ghosts to play mobs", "type" = "boolean", "value" = "No"),
"minplayers" = list("desc" = "Minimum number of ghosts", "type" = "number", "value" = 1),
"playersonly" = list("desc" = "Only spawn ghost-controlled mobs", "type" = "boolean", "value" = "No"),
"ghostpoll" = list("desc" = "Ghost poll question", "type" = "string", "value" = "Do you want to play as %TYPE% portal invader?"),
"delay" = list("desc" = "Time between portals, in deciseconds", "type" = "number", "value" = 50),
"color" = list("desc" = "Portal color", "type" = "color", "value" = "#00FF00"),
"playlightning" = list("desc" = "Play lightning sounds on announcement", "type" = "boolean", "value" = "Yes"),
"announce_players" = list("desc" = "Make an announcement", "type" = "boolean", "value" = "Yes"),
"announcement" = list("desc" = "Announcement", "type" = "string", "value" = "Massive bluespace anomaly detected en route to %STATION%. Brace for impact."),
)
)
message_admins("[key_name(usr)] is creating a custom portal storm...")
var/list/prefreturn = presentpreflikepicker(usr,"Customize Portal Storm", "Customize Portal Storm", Button1="Ok", width = 600, StealFocus = 1,Timeout = 0, settings=settings)
if (prefreturn["button"] == 1)
var/list/prefs = settings["mainsettings"]
if (prefs["amount"]["value"] < 1 || prefs["portalnum"]["value"] < 1)
to_chat(usr, "Number of portals and mobs to spawn must be at least 1")
return
var/mob/pathToSpawn = prefs["typepath"]["value"]
if (!ispath(pathToSpawn))
pathToSpawn = text2path(pathToSpawn)
if (!ispath(pathToSpawn))
to_chat(usr, "Invalid path [pathToSpawn]")
return
var/list/candidates = list()
if (prefs["offerghosts"]["value"] == "Yes")
candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR)
if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"])
message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]")
return
if (prefs["announce_players"]["value"] == "Yes")
portalAnnounce(prefs["announcement"]["value"], (prefs["playlightning"]["value"] == "Yes" ? TRUE : FALSE))
var/mutable_appearance/storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER)
storm.color = prefs["color"]["value"]
message_admins("[key_name_admin(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]")
log_admin("[key_name(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]")
var/outfit = prefs["humanoutfit"]["value"]
if (!ispath(outfit))
outfit = text2path(outfit)
for (var/i in 1 to prefs["portalnum"]["value"])
if (length(candidates)) // if we're spawning players, gotta be a little tricky and also not spawn players on top of NPCs
var/ghostcandidates = list()
for (var/j in 1 to min(prefs["amount"]["value"], length(candidates)))
ghostcandidates += pick_n_take(candidates)
addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, length(ghostcandidates), storm, ghostcandidates, outfit), i*prefs["delay"]["value"])
else if (prefs["playersonly"]["value"] != "Yes")
addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, prefs["amount"]["value"], storm, null, outfit), i*prefs["delay"]["value"])
if(E)
E.processing = FALSE
if(E.announceWhen>0)
if(alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No") == "No")
E.announceWhen = -1
E.processing = TRUE
if (usr)
log_admin("[key_name(usr)] used secret [item]")
if (ok)
to_chat(world, text("<B>A secret has been activated by []!</B>", usr.key))
/proc/portalAnnounce(announcement, playlightning)
set waitfor = 0
if (playlightning)
sound_to_playing_players('sound/magic/lightning_chargeup.ogg')
sleep(80)
priority_announce(replacetext(announcement, "%STATION%", station_name()))
if (playlightning)
sleep(20)
sound_to_playing_players('sound/magic/lightningbolt.ogg')
/proc/doPortalSpawn(turf/loc, mobtype, numtospawn, portal_appearance, players, humanoutfit)
for (var/i in 1 to numtospawn)
var/mob/spawnedMob = new mobtype(loc)
if (length(players))
var/mob/chosen = players[1]
if (chosen.client)
chosen.client.prefs.copy_to(spawnedMob)
chosen.transfer_ckey(spawnedMob)
players -= chosen
if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit))
var/mob/living/carbon/human/H = spawnedMob
H.equipOutfit(humanoutfit)
var/turf/T = get_step(loc, SOUTHWEST)
flick_overlay_static(portal_appearance, T, 15)
playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), 1)
-603
View File
@@ -1,603 +0,0 @@
/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
if(!type)
return
var/target_ckey = ckey(target_key)
if(!target_key && (type == "note" || type == "message" || type == "watchlist entry"))
var/new_key = input(usr,"Who would you like to create a [type] for?","Enter a key or ckey",null) as null|text
if(!new_key)
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]'")
if(!query_find_ckey.warn_execute())
qdel(query_find_ckey)
return
if(!query_find_ckey.NextRow())
if(alert(usr, "[new_key]/([new_ckey]) has not been seen before, are you sure you want to create a [type] for them?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes")
qdel(query_find_ckey)
return
qdel(query_find_ckey)
target_ckey = new_ckey
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")
secret = 1
if("No")
secret = 0
else
return
if(isnull(expiry))
if(alert(usr, "Set an expiry time? Expired messages are hidden like deleted ones.", "Expiry time?", "Yes", "No", "Cancel") == "Yes")
var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons.", "Set expiry time", SQLtime()) as null|text
if(!expire_time)
return
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
if(!query_validate_expire_time.warn_execute())
qdel(query_validate_expire_time)
return
if(query_validate_expire_time.NextRow())
var/checktime = text2num(query_validate_expire_time.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
qdel(query_validate_expire_time)
return
expiry = query_validate_expire_time.item[1]
qdel(query_validate_expire_time)
if(type == "note" && isnull(note_severity))
note_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!note_severity)
return
note_severity = sanitizeSQL(note_severity)
var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]', [expiry ? "'[expiry]'" : "NULL"], [note_severity ? "'[note_severity]'" : "NULL"])")
var/pm = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]: [text]"
var/header = "[key_name_admin(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]"
if(!query_create_message.warn_execute())
qdel(query_create_message)
return
qdel(query_create_message)
if(logged)
log_admin_private(pm)
message_admins("[header]:<br>[text]")
admin_ticket_log(target_ckey, "<font color='blue'>[header]</font>")
admin_ticket_log(target_ckey, text)
if(browse)
browse_messages("[type]")
else
browse_messages(target_ckey = target_ckey, agegate = TRUE)
/proc/delete_message(message_id, logged = 1, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/type
var/target_key
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")
if(!query_find_del_message.warn_execute())
qdel(query_find_del_message)
return
if(query_find_del_message.NextRow())
type = query_find_del_message.item[1]
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]")
if(!query_del_message.warn_execute())
qdel(query_del_message)
return
qdel(query_del_message)
if(logged)
var/m1 = "[user_key_name] has deleted a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for" : " made by"] [target_key]: [text]"
var/m2 = "[user_name_admin] has deleted a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for" : " made by"] [target_key]:<br>[text]"
log_admin_private(m1)
message_admins(m2)
if(browse)
browse_messages("[type]")
else
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
/proc/edit_message(message_id, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_message.warn_execute())
qdel(query_find_edit_message)
return
if(query_find_edit_message.NextRow())
var/type = query_find_edit_message.item[1]
var/target_key = query_find_edit_message.item[2]
var/admin_key = query_find_edit_message.item[3]
var/old_text = query_find_edit_message.item[4]
var/new_text = input("Input new [type]", "New [type]", "[old_text]") as null|message
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")
if(!query_edit_message.warn_execute())
qdel(query_edit_message)
return
qdel(query_edit_message)
log_admin_private("[kn] has edited a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_text] to [new_text]")
message_admins("[kna] has edited a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from<br>[old_text]<br>to<br>[new_text]")
if(browse)
browse_messages("[type]")
else
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_message)
/proc/edit_message_expiry(message_id, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_expiry_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), expire_timestamp FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_expiry_message.warn_execute())
qdel(query_find_edit_expiry_message)
return
if(query_find_edit_expiry_message.NextRow())
var/type = query_find_edit_expiry_message.item[1]
var/target_key = query_find_edit_expiry_message.item[2]
var/admin_key = query_find_edit_expiry_message.item[3]
var/old_expiry = query_find_edit_expiry_message.item[4]
var/new_expiry
var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons. Enter -1 to remove expiry time.", "Set expiry time", old_expiry) as null|text
if(!expire_time)
qdel(query_find_edit_expiry_message)
return
if(expire_time == "-1")
new_expiry = "non-expiring"
else
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time_edit = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
if(!query_validate_expire_time_edit.warn_execute())
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
return
if(query_validate_expire_time_edit.NextRow())
var/checktime = text2num(query_validate_expire_time_edit.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
return
new_expiry = query_validate_expire_time_edit.item[1]
qdel(query_validate_expire_time_edit)
var/edit_text = sanitizeSQL("Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]<hr>")
var/datum/DBQuery/query_edit_message_expiry = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET expire_timestamp = [expire_time == "-1" ? "NULL" : "'[new_expiry]'"], lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
if(!query_edit_message_expiry.warn_execute())
qdel(query_edit_message_expiry)
qdel(query_find_edit_expiry_message)
return
qdel(query_edit_message_expiry)
log_admin_private("[kn] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]")
message_admins("[kna] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]")
if(browse)
browse_messages("[type]")
else
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_expiry_message)
/proc/edit_message_severity(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_note_severity = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), severity FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_edit_note_severity.warn_execute())
qdel(query_find_edit_note_severity)
return
if(query_find_edit_note_severity.NextRow())
var/type = query_find_edit_note_severity.item[1]
var/target_key = query_find_edit_note_severity.item[2]
var/admin_key = query_find_edit_note_severity.item[3]
var/old_severity = query_find_edit_note_severity.item[4]
if(!old_severity)
old_severity = "NA"
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = sanitizeSQL(usr.ckey)
var/new_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("high", "medium", "minor", "none") //lowercase for edit log consistency
if(!new_severity)
qdel(query_find_edit_note_severity)
return
new_severity = sanitizeSQL(new_severity)
var/edit_text = sanitizeSQL("Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]<hr>")
var/datum/DBQuery/query_edit_note_severity = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET severity = '[new_severity]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
if(!query_edit_note_severity.warn_execute(async = TRUE))
qdel(query_edit_note_severity)
qdel(qdel(query_find_edit_note_severity))
return
qdel(query_edit_note_severity)
log_admin_private("[kn] has edited the severity of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]")
message_admins("[kna] has edited the severity time of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]")
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_edit_note_severity)
/proc/toggle_message_secrecy(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
if(!query_find_message_secret.warn_execute())
qdel(query_find_message_secret)
return
if(query_find_message_secret.NextRow())
var/type = query_find_message_secret.item[1]
var/target_key = query_find_message_secret.item[2]
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]")
if(!query_message_secret.warn_execute())
qdel(query_find_message_secret)
qdel(query_message_secret)
return
qdel(query_message_secret)
log_admin_private("[kn] has toggled [target_key]'s [type] made by [admin_key] to [secret ? "not secret" : "secret"]")
message_admins("[kna] has toggled [target_key]'s [type] made by [admin_key] to [secret ? "not secret" : "secret"]")
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)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
var/list/output = list()
var/ruler = "<hr style='background:#000000; border:0; height:3px'>"
var/list/navbar = list("<a href='?_src_=holder;[HrefToken()];nonalpha=1'>All</a><a href='?_src_=holder;[HrefToken()];nonalpha=2'>#</a>")
for(var/letter in GLOB.alphabet)
navbar += "<a href='?_src_=holder;[HrefToken()];showmessages=[letter]'>[letter]</a>"
navbar += "<a href='?_src_=holder;[HrefToken()];showmemo=1'>Memos</a><a href='?_src_=holder;[HrefToken()];showwatch=1'>Watchlist</a>"
navbar += "<br><form method='GET' name='search' action='?'>\
<input type='hidden' name='_src_' value='holder'>\
[HrefTokenFormField()]\
<input type='text' name='searchmessages' value='[index]'>\
<input type='submit' value='Search'></form>"
if(!linkless)
output = navbar
if(type == "memo" || type == "watchlist entry")
if(type == "memo")
output += "<h2><center>Admin memos</h2>"
output += "<a href='?_src_=holder;[HrefToken()];addmemo=1'>Add memo</a></center>"
else if(type == "watchlist entry")
output += "<h2><center>Watchlist entries</h2>"
output += "<a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a>"
if(filter)
output += "<a href='?_src_=holder;[HrefToken()];showwatch=1'>Unfilter clients</a></center>"
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)")
if(!query_get_type_messages.warn_execute())
qdel(query_get_type_messages)
return
while(query_get_type_messages.NextRow())
if(QDELETED(usr))
return
var/id = query_get_type_messages.item[1]
var/t_key = query_get_type_messages.item[2]
var/t_ckey = query_get_type_messages.item[3]
if(type == "watchlist entry" && filter && !(t_ckey in GLOB.directory))
continue
var/admin_key = query_get_type_messages.item[4]
var/text = query_get_type_messages.item[5]
var/timestamp = query_get_type_messages.item[6]
var/server = query_get_type_messages.item[7]
var/editor_key = query_get_type_messages.item[8]
var/expire_timestamp = query_get_type_messages.item[9]
output += "<b>"
if(type == "watchlist entry")
output += "[t_key] | "
output += "[timestamp] | [server] | [admin_key]"
if(expire_timestamp)
output += " | Expires [expire_timestamp]"
output += "</b>"
output += " <a href='?_src_=holder;[HrefToken()];editmessageexpiryempty=[id]'>Change Expiry Time</a>"
output += " <a href='?_src_=holder;[HrefToken()];deletemessageempty=[id]'>Delete</a>"
output += " <a href='?_src_=holder;[HrefToken()];editmessageempty=[id]'>Edit</a>"
if(editor_key)
output += " <font size='2'>Last edit by [editor_key] <a href='?_src_=holder;[HrefToken()];messageedits=[id]'>(Click here to see edit log)</a></font>"
output += "<br>[text]<hr style='background:#000000; border:0; height:1px'>"
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")
if(!query_get_messages.warn_execute())
qdel(query_get_messages)
return
var/list/messagedata = list()
var/list/watchdata = list()
var/list/notedata = list()
var/skipped = 0
while(query_get_messages.NextRow())
if(QDELETED(usr))
return
type = query_get_messages.item[1]
if(type == "memo")
continue
var/secret = text2num(query_get_messages.item[2])
if(linkless && secret)
continue
var/id = query_get_messages.item[3]
var/admin_key = query_get_messages.item[4]
var/text = query_get_messages.item[5]
var/timestamp = query_get_messages.item[6]
var/server = query_get_messages.item[7]
var/editor_key = query_get_messages.item[8]
var/age = text2num(query_get_messages.item[9])
target_key = query_get_messages.item[10]
var/expire_timestamp = query_get_messages.item[11]
var/severity = query_get_messages.item[12]
var/alphatext = ""
var/nsd = CONFIG_GET(number/note_stale_days)
var/nfd = CONFIG_GET(number/note_fresh_days)
if (agegate && type == "note" && isnum(nsd) && isnum(nfd) && nsd > nfd)
var/alpha = CLAMP(100 - (age - nfd) * (85 / (nsd - nfd)), 15, 100)
if (alpha < 100)
if (alpha <= 15)
if (skipped)
skipped++
continue
alpha = 10
skipped = TRUE
alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];"
var/list/data = list("<div style='margin:0px;[alphatext]'><p class='severity'>")
if(severity)
data += "<img src='[severity]_button.png' height='24' width='24'></img> "
data += "<b>[timestamp] | [server] | [admin_key][secret ? " | <i>- Secret</i>" : ""]"
if(expire_timestamp)
data += " | Expires [expire_timestamp]"
data += "</b></p><center>"
if(!linkless)
if(type == "note")
if(severity)
data += "<a href='?_src_=holder;[HrefToken()];editmessageseverity=[id]'>[severity=="none" ? "No" : "[capitalize(severity)]"] Severity</a>"
else
data += "<a href='?_src_=holder;[HrefToken()];editmessageseverity=[id]'>N/A Severity</a>"
data += " <a href='?_src_=holder;[HrefToken()];editmessageexpiry=[id]'>Change Expiry Time</a>"
data += " <a href='?_src_=holder;[HrefToken()];deletemessage=[id]'>Delete</a>"
if(type == "note")
data += " <a href='?_src_=holder;[HrefToken()];secretmessage=[id]'>[secret ? "<b>Secret</b>" : "Not secret"]</a>"
if(type == "message sent")
data += " <font size='2'>Message has been sent</font>"
if(editor_key)
data += "|"
else
data += " <a href='?_src_=holder;[HrefToken()];editmessage=[id]'>Edit</a>"
if(editor_key)
data += " <font size='2'>Last edit by [editor_key] <a href='?_src_=holder;[HrefToken()];messageedits=[id]'>(Click here to see edit log)</a></font>"
data += "</div></center>"
data += "<p style='[alphatext]'>[text]</p><hr style='background:#000000; border:0; height:1px; [alphatext]'>"
switch(type)
if("message")
messagedata += data
if("message sent")
messagedata += data
if("watchlist entry")
watchdata += data
if("note")
notedata += data
qdel(query_get_messages)
if(!target_key)
var/datum/DBQuery/query_get_message_key = SSdbcore.NewQuery("SELECT byond_key FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'")
if(!query_get_message_key.warn_execute())
qdel(query_get_message_key)
return
if(query_get_message_key.NextRow())
target_key = query_get_message_key.item[1]
qdel(query_get_message_key)
output += "<h2><center>[target_key]</center></h2><center>"
if(!linkless)
output += "<a href='?_src_=holder;[HrefToken()];addnote=[target_key]'>Add note</a>"
output += " <a href='?_src_=holder;[HrefToken()];addmessage=[target_key]'>Add message</a>"
output += " <a href='?_src_=holder;[HrefToken()];addwatch=[target_key]'>Add to watchlist</a>"
output += " <a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>Refresh page</a></center>"
else
output += " <a href='?_src_=holder;[HrefToken()];showmessageckeylinkless=[target_ckey]'>Refresh page</a></center>"
output += ruler
if(messagedata)
output += "<h2>Messages</h2>"
output += messagedata
if(watchdata)
output += "<h2>Watchlist</h2>"
output += watchdata
if(notedata)
output += "<h2>Notes</h2>"
output += notedata
if(!linkless)
if (agegate)
if (skipped) //the first skipped message is still shown so that we can put this link over it.
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1' style='position: relative; top: -3em;'>Show [skipped] hidden messages</a></center>"
else
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey];showall=1'>Show All</a></center>"
else
output += "<center><a href='?_src_=holder;[HrefToken()];showmessageckey=[target_ckey]'>Hide Old</a></center>"
if(index)
var/search
output += "<center><a href='?_src_=holder;[HrefToken()];addmessageempty=1'>Add message</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>Add note</a></center>"
output += ruler
if(!isnum(index))
index = sanitizeSQL(index)
switch(index)
if(1)
search = "^."
if(2)
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")
if(!query_list_messages.warn_execute())
qdel(query_list_messages)
return
while(query_list_messages.NextRow())
if(QDELETED(usr))
return
var/index_ckey = query_list_messages.item[1]
var/index_key = query_list_messages.item[2]
if(!index_key)
index_key = index_ckey
output += "<a href='?_src_=holder;[HrefToken()];showmessageckey=[index_ckey]'>[index_key]</a><br>"
qdel(query_list_messages)
else if(!type && !target_ckey && !index)
output += "<center><a 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
var/datum/browser/browser = new(usr, "Note panel", "Manage player notes", 1000, 500)
var/datum/asset/notes_assets = get_asset_datum(/datum/asset/simple/notes)
notes_assets.send(usr.client)
browser.set_content(jointext(output, ""))
browser.open()
/proc/get_message_output(type, target_ckey)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
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)
if(!query_get_message_output.warn_execute())
qdel(query_get_message_output)
return
while(query_get_message_output.NextRow())
var/message_id = query_get_message_output.item[1]
var/admin_key = query_get_message_output.item[2]
var/text = query_get_message_output.item[3]
var/timestamp = query_get_message_output.item[4]
var/editor_key = query_get_message_output.item[5]
switch(type)
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]")
if(!query_message_read.warn_execute())
qdel(query_get_message_output)
qdel(query_message_read)
return
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]")
if("memo")
output += "<span class='memo'>Memo by <span class='prefix'>[admin_key]</span> on [timestamp]"
if(editor_key)
output += "<br><span class='memoedit'>Last edit by [editor_key] <A href='?_src_=holder;[HrefToken()];messageedits=[message_id]'>(Click here to see edit log)</A></span>"
output += "<br>[text]</span><br>"
qdel(query_get_message_output)
return output
#define NOTESFILE "data/player_notes.sav"
//if the AUTOCONVERT_NOTES is turned on, anytime a player connects this will be run to try and add all their notes to the databas
/proc/convert_notes_sql(ckey)
if(!fexists(NOTESFILE))
return
var/savefile/notesfile = new(NOTESFILE)
if(!notesfile)
log_game("Error: Cannot access [NOTESFILE]")
return
notesfile.cd = "/[ckey]"
while(!notesfile.eof)
var/notetext
notesfile >> notetext
var/server
var/ssqlname = CONFIG_GET(string/serversqlname)
if (ssqlname)
server = ssqlname
var/regex/note = new("^(\\d{2}-\\w{3}-\\d{4}) \\| (.+) ~(\\w+)$", "i")
note.Find(notetext)
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')")
if(!query_convert_time.Execute())
qdel(query_convert_time)
return
if(query_convert_time.NextRow())
timestamp = query_convert_time.item[1]
qdel(query_convert_time)
if(ckey && notetext && timestamp && admin_ckey && server)
create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0, null, 0, 0)
notesfile.cd = "/"
notesfile.dir.Remove(ckey)
/*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")
var/savefile/notesfile = new(NOTESFILE)
if(!notesfile)
log_game("Error: Cannot access [NOTESFILE]")
return
notesfile.cd = "/"
for(var/ckey in notesfile.dir)
convert_notes_sql(ckey)
to_chat(world, "Deleting NOTESFILE")
fdel(NOTESFILE)
to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES")*/
#undef NOTESFILE
File diff suppressed because it is too large Load Diff
-699
View File
@@ -1,699 +0,0 @@
/client/var/adminhelptimerid = 0 //a timer id for returning the ahelp verb
/client/var/datum/admin_help/current_ticket //the current ticket the (usually) not-admin client is dealing with
//
//TICKET MANAGER
//
GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/datum/admin_help_tickets
var/list/active_tickets = list()
var/list/closed_tickets = list()
var/list/resolved_tickets = list()
var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE)
var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED)
var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED)
/datum/admin_help_tickets/Destroy()
QDEL_LIST(active_tickets)
QDEL_LIST(closed_tickets)
QDEL_LIST(resolved_tickets)
QDEL_NULL(astatclick)
QDEL_NULL(cstatclick)
QDEL_NULL(rstatclick)
return ..()
/datum/admin_help_tickets/proc/TicketByID(id)
var/list/lists = list(active_tickets, closed_tickets, resolved_tickets)
for(var/I in lists)
for(var/J in I)
var/datum/admin_help/AH = J
if(AH.id == id)
return J
/datum/admin_help_tickets/proc/TicketsByCKey(ckey)
. = list()
var/list/lists = list(active_tickets, closed_tickets, resolved_tickets)
for(var/I in lists)
for(var/J in I)
var/datum/admin_help/AH = J
if(AH.initiator_ckey == ckey)
. += AH
//private
/datum/admin_help_tickets/proc/ListInsert(datum/admin_help/new_ticket)
var/list/ticket_list
switch(new_ticket.state)
if(AHELP_ACTIVE)
ticket_list = active_tickets
if(AHELP_CLOSED)
ticket_list = closed_tickets
if(AHELP_RESOLVED)
ticket_list = resolved_tickets
else
CRASH("Invalid ticket state: [new_ticket.state]")
var/num_closed = ticket_list.len
if(num_closed)
for(var/I in 1 to num_closed)
var/datum/admin_help/AH = ticket_list[I]
if(AH.id > new_ticket.id)
ticket_list.Insert(I, new_ticket)
return
ticket_list += new_ticket
//opens the ticket listings for one of the 3 states
/datum/admin_help_tickets/proc/BrowseTickets(state)
var/list/l2b
var/title
switch(state)
if(AHELP_ACTIVE)
l2b = active_tickets
title = "Active Tickets"
if(AHELP_CLOSED)
l2b = closed_tickets
title = "Closed Tickets"
if(AHELP_RESOLVED)
l2b = resolved_tickets
title = "Resolved Tickets"
if(!l2b)
return
var/list/dat = list("<html><head><title>[title]</title></head>")
dat += "<A href='?_src_=holder;[HrefToken()];ahelp_tickets=[state]'>Refresh</A><br><br>"
for(var/I in l2b)
var/datum/admin_help/AH = I
dat += "<span class='adminnotice'><span class='adminhelp'>Ticket #[AH.id]</span>: <A href='?_src_=holder;[HrefToken()];ahelp=[REF(AH)];ahelp_action=ticket'>[AH.initiator_key_name]: [AH.name]</A></span><br>"
usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480")
//Tickets statpanel
/datum/admin_help_tickets/proc/stat_entry()
var/num_disconnected = 0
stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
for(var/I in active_tickets)
var/datum/admin_help/AH = I
if(AH.initiator)
stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update())
else
++num_disconnected
if(num_disconnected)
stat("Disconnected:", astatclick.update("[num_disconnected]"))
stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]"))
stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
//Reassociate still open ticket if one exists
/datum/admin_help_tickets/proc/ClientLogin(client/C)
C.current_ticket = CKey2ActiveTicket(C.ckey)
if(C.current_ticket)
C.current_ticket.initiator = C
C.current_ticket.AddInteraction("Client reconnected.")
//Dissasociate ticket
/datum/admin_help_tickets/proc/ClientLogout(client/C)
if(C.current_ticket)
C.current_ticket.AddInteraction("Client disconnected.")
C.current_ticket.initiator = null
C.current_ticket = null
//Get a ticket given a ckey
/datum/admin_help_tickets/proc/CKey2ActiveTicket(ckey)
for(var/I in active_tickets)
var/datum/admin_help/AH = I
if(AH.initiator_ckey == ckey)
return AH
//
//TICKET LIST STATCLICK
//
/obj/effect/statclick/ticket_list
var/current_state
/obj/effect/statclick/ticket_list/New(loc, name, state)
current_state = state
..()
/obj/effect/statclick/ticket_list/Click()
GLOB.ahelp_tickets.BrowseTickets(current_state)
//
//TICKET DATUM
//
/datum/admin_help
var/id
var/name
var/state = AHELP_ACTIVE
var/opened_at
var/closed_at
var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked
var/initiator_ckey
var/initiator_key_name
var/heard_by_no_admins = FALSE
var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log()
var/obj/effect/statclick/ahelp/statclick
var/static/ticket_counter = 0
//call this on its own to create a ticket, don't manually assign current_ticket
//msg is the title of the ticket: usually the ahelp text
//is_bwoink is TRUE if this ticket was started by an admin PM
/datum/admin_help/New(msg, client/C, is_bwoink)
//clean the input msg
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg || !C || !C.mob)
qdel(src)
return
id = ++ticket_counter
opened_at = world.time
name = msg
initiator = C
initiator_ckey = initiator.ckey
initiator_key_name = key_name(initiator, FALSE, TRUE)
if(initiator.current_ticket) //This is a bug
stack_trace("Multiple ahelp current_tickets")
initiator.current_ticket.AddInteraction("Ticket erroneously left open by code")
initiator.current_ticket.Close()
initiator.current_ticket = src
TimeoutVerb()
statclick = new(null, src)
_interactions = list()
if(is_bwoink)
AddInteraction("<font color='blue'>[key_name_admin(usr)] PM'd [LinkedReplyName()]</font>")
message_admins("<font color='blue'>Ticket [TicketHref("#[id]")] created</font>")
else
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]")
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>")
heard_by_no_admins = TRUE
GLOB.ahelp_tickets.active_tickets += src
/datum/admin_help/Destroy()
RemoveActive()
GLOB.ahelp_tickets.closed_tickets -= src
GLOB.ahelp_tickets.resolved_tickets -= src
return ..()
/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)]")
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
//Removes the ahelp verb and returns it after 2 minutes
/datum/admin_help/proc/TimeoutVerb()
initiator.verbs -= /client/verb/adminhelp
initiator.adminhelptimerid = addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 1200, TIMER_STOPPABLE) //2 minute cooldown of admin helps
//private
/datum/admin_help/proc/FullMonty(ref_src)
if(!ref_src)
ref_src = "[REF(src)]"
. = ADMIN_FULLMONTY_NONAME(initiator.mob)
if(state == AHELP_ACTIVE)
. += ClosureLinks(ref_src)
//private
/datum/admin_help/proc/ClosureLinks(ref_src)
if(!ref_src)
ref_src = "[REF(src)]"
. = " (<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=reject'>REJT</A>)"
. += " (<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=icissue'>IC</A>)"
. += " (<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=close'>CLOSE</A>)"
. += " (<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=resolve'>RSLVE</A>)"
. += " (<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=handleissue'>HANDLE</A>)"
//private
/datum/admin_help/proc/LinkedReplyName(ref_src)
if(!ref_src)
ref_src = "[REF(src)]"
return "<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=reply'>[initiator_key_name]</A>"
//private
/datum/admin_help/proc/TicketHref(msg, ref_src, action = "ticket")
if(!ref_src)
ref_src = "[REF(src)]"
return "<A HREF='?_src_=holder;[HrefToken(TRUE)];ahelp=[ref_src];ahelp_action=[action]'>[msg]</A>"
//message from the initiator without a target, all admins will see this
//won't bug irc
/datum/admin_help/proc/MessageNoRecipient(msg)
var/ref_src = "[REF(src)]"
//Message to be sent to all admins
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> <span class='linkify'>[keywords_lookup(msg)]</span></span>"
AddInteraction("<font color='red'>[LinkedReplyName(ref_src)]: [msg]</font>")
//send this msg to all admins
for(var/client/X in GLOB.admins)
if(X.prefs.toggles & SOUND_ADMINHELP)
SEND_SOUND(X, sound('sound/effects/adminhelp.ogg'))
window_flash(X, ignorepref = TRUE)
to_chat(X, admin_msg)
//show it to the person adminhelping too
to_chat(initiator, "<span class='adminnotice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>")
//Reopen a closed ticket
/datum/admin_help/proc/Reopen()
if(state == AHELP_ACTIVE)
to_chat(usr, "<span class='warning'>This ticket is already open.</span>")
return
if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey))
to_chat(usr, "<span class='warning'>This user already has an active ticket, cannot reopen this one.</span>")
return
statclick = new(null, src)
GLOB.ahelp_tickets.active_tickets += src
GLOB.ahelp_tickets.closed_tickets -= src
GLOB.ahelp_tickets.resolved_tickets -= src
switch(state)
if(AHELP_CLOSED)
SSblackbox.record_feedback("tally", "ahelp_stats", -1, "closed")
if(AHELP_RESOLVED)
SSblackbox.record_feedback("tally", "ahelp_stats", -1, "resolved")
state = AHELP_ACTIVE
closed_at = null
if(initiator)
initiator.current_ticket = src
AddInteraction("<font color='purple'>Reopened by [key_name_admin(usr)]</font>")
var/msg = "<span class='adminhelp'>Ticket [TicketHref("#[id]")] reopened by [key_name_admin(usr)].</span>"
message_admins(msg)
log_admin_private(msg)
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "reopened")
TicketPanel() //can only be done from here, so refresh it
//private
/datum/admin_help/proc/RemoveActive()
if(state != AHELP_ACTIVE)
return
closed_at = world.time
QDEL_NULL(statclick)
GLOB.ahelp_tickets.active_tickets -= src
if(initiator && initiator.current_ticket == src)
initiator.current_ticket = null
//Mark open ticket as closed/meme
/datum/admin_help/proc/Close(key_name = key_name_admin(usr), silent = FALSE)
if(state != AHELP_ACTIVE)
return
RemoveActive()
state = AHELP_CLOSED
GLOB.ahelp_tickets.ListInsert(src)
to_chat(initiator, "<span class='adminhelp'>Ticket closed by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"].</span>")
AddInteraction("<font color='red'>Closed by [key_name].</font>")
if(!silent)
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "closed")
var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name]."
message_admins(msg)
log_admin_private(msg)
//Mark open ticket as resolved/legitimate, returns ahelp verb
/datum/admin_help/proc/Resolve(key_name = key_name_admin(usr), silent = FALSE)
if(state != AHELP_ACTIVE)
return
RemoveActive()
state = AHELP_RESOLVED
GLOB.ahelp_tickets.ListInsert(src)
addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 50)
AddInteraction("<font color='green'>Resolved by [key_name].</font>")
to_chat(initiator, "<span class='adminhelp'>Your ticket has been resolved by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]. The Adminhelp verb will be returned to you shortly.</span>")
if(!silent)
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "resolved")
var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name]"
message_admins(msg)
log_admin_private(msg)
//Close and return ahelp verb, use if ticket is incoherent
/datum/admin_help/proc/Reject(key_name = key_name_admin(usr))
if(state != AHELP_ACTIVE)
return
if(initiator)
initiator.giveadminhelpverb()
SEND_SOUND(initiator, sound('sound/effects/adminhelp.ogg'))
to_chat(initiator, "<font color='red' size='4'><b>- AdminHelp Rejected by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]! -</b></font>")
to_chat(initiator, "<font color='red'><b>Your admin help was rejected.</b> The adminhelp verb has been returned to you so that you may try again.</font>")
to_chat(initiator, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting.")
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "rejected")
var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name]"
message_admins(msg)
log_admin_private(msg)
AddInteraction("Rejected by [key_name].")
Close(silent = TRUE)
//Resolve ticket with IC Issue message
/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr))
if(state != AHELP_ACTIVE)
return
var/msg = "<font color='red' size='4'><b>- AdminHelp marked as IC issue by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]! -</b></font><br>"
msg += "<font color='red'><b>Losing is part of the game!</b></font><br>"
msg += "<font color='red'>It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round.</font>"
if(initiator)
to_chat(initiator, msg)
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "IC")
msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name]"
message_admins(msg)
log_admin_private(msg)
AddInteraction("Marked as IC issue by [key_name]")
Resolve(silent = TRUE)
//Let the initiator know their ahelp is being handled
/datum/admin_help/proc/HandleIssue(key_name = key_name_admin(usr))
if(state != AHELP_ACTIVE)
return
var/msg = "<span class ='adminhelp'>Your ticket is now being handled by an admin. Please be patient.</span>"
if(initiator)
to_chat(initiator, msg)
SSblackbox.record_feedback("tally", "ahelp_stats", 1, "handling")
msg = "Ticket [TicketHref("#[id]")] is being handled by [key_name]"
message_admins(msg)
log_admin_private(msg)
AddInteraction("Being handled by [key_name]")
//Show the ticket panel
/datum/admin_help/proc/TicketPanel()
var/list/dat = list("<html><head><title>Ticket #[id]</title></head>")
var/ref_src = "[REF(src)]"
dat += "<h4>Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]</h4>"
dat += "<b>State: "
switch(state)
if(AHELP_ACTIVE)
dat += "<font color='red'>OPEN</font>"
if(AHELP_RESOLVED)
dat += "<font color='green'>RESOLVED</font>"
if(AHELP_CLOSED)
dat += "CLOSED"
else
dat += "UNKNOWN"
dat += "</b>[FOURSPACES][TicketHref("Refresh", ref_src)][FOURSPACES][TicketHref("Re-Title", ref_src, "retitle")]"
if(state != AHELP_ACTIVE)
dat += "[FOURSPACES][TicketHref("Reopen", ref_src, "reopen")]"
dat += "<br><br>Opened at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
if(closed_at)
dat += "<br>Closed at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
dat += "<br><br>"
if(initiator)
dat += "<b>Actions:</b> [FullMonty(ref_src)]<br>"
else
dat += "<b>DISCONNECTED</b>[FOURSPACES][ClosureLinks(ref_src)]<br>"
dat += "<br><b>Log:</b><br><br>"
for(var/I in _interactions)
dat += "[I]<br>"
usr << browse(dat.Join(), "window=ahelp[id];size=620x480")
/datum/admin_help/proc/Retitle()
var/new_title = input(usr, "Enter a title for the ticket", "Rename Ticket", name) as text|null
if(new_title)
name = new_title
//not saying the original name cause it could be a long ass message
var/msg = "Ticket [TicketHref("#[id]")] titled [name] by [key_name_admin(usr)]"
message_admins(msg)
log_admin_private(msg)
TicketPanel() //we have to be here to do this
//Forwarded action from admin/Topic
/datum/admin_help/proc/Action(action)
testing("Ahelp action: [action]")
switch(action)
if("ticket")
TicketPanel()
if("retitle")
Retitle()
if("reject")
Reject()
if("reply")
usr.client.cmd_ahelp_reply(initiator)
if("icissue")
ICIssue()
if("close")
Close()
if("resolve")
Resolve()
if("handleissue")
HandleIssue()
if("reopen")
Reopen()
//
// TICKET STATCLICK
//
/obj/effect/statclick/ahelp
var/datum/admin_help/ahelp_datum
/obj/effect/statclick/ahelp/Initialize(mapload, datum/admin_help/AH)
ahelp_datum = AH
. = ..()
/obj/effect/statclick/ahelp/update()
return ..(ahelp_datum.name)
/obj/effect/statclick/ahelp/Click()
ahelp_datum.TicketPanel()
/obj/effect/statclick/ahelp/Destroy()
ahelp_datum = null
return ..()
//
// CLIENT PROCS
//
/client/proc/giveadminhelpverb()
src.verbs |= /client/verb/adminhelp
deltimer(adminhelptimerid)
adminhelptimerid = 0
// Used for methods where input via arg doesn't work
/client/proc/get_adminhelp()
var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text
adminhelp(msg)
/client/verb/adminhelp(msg as text)
set category = "Admin"
set name = "Adminhelp"
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>")
return
//handle muting and automuting
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<span class='danger'>Error: Admin-PM: You cannot send adminhelps (Muted).</span>")
return
if(handle_spam_prevention(msg,MUTE_ADMINHELP))
return
msg = trim(msg)
if(!msg)
return
SSblackbox.record_feedback("tally", "admin_verb", 1, "Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
if(current_ticket)
if(alert(usr, "You already have a ticket open. Is this for the same issue?",,"Yes","No") != "No")
if(current_ticket)
current_ticket.MessageNoRecipient(msg)
current_ticket.TimeoutVerb()
return
else
to_chat(usr, "<span class='warning'>Ticket not found, creating new one...</span>")
else
current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.")
current_ticket.Close()
new /datum/admin_help(msg, src, FALSE)
//
// LOGGING
//
//Use this proc when an admin takes action that may be related to an open ticket on what
//what can be a client, ckey, or mob
/proc/admin_ticket_log(what, message)
var/client/C
var/mob/Mob = what
if(istype(Mob))
C = Mob.client
else
C = what
if(istype(C) && C.current_ticket)
C.current_ticket.AddInteraction(message)
return C.current_ticket
if(istext(what)) //ckey
var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what)
if(AH)
AH.AddInteraction(message)
return AH
//
// HELPER PROCS
//
/proc/get_admin_counts(requiredflags = R_BAN)
. = 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))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X
else if(X.holder.fakekey)
.["stealth"] += X
else
.["present"] += X
/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
var/list/adm = get_admin_counts(requiredflags)
var/list/activemins = adm["present"]
. = activemins.len
if(. <= 0)
var/final = ""
var/list/afkmins = adm["afk"]
var/list/stealthmins = adm["stealth"]
var/list/powerlessmins = adm["noflags"]
var/list/allmins = adm["total"]
if(!afkmins.len && !stealthmins.len && !powerlessmins.len)
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)
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)
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/list/servers = CONFIG_GET(keyed_list/cross_server)
for(var/I in servers)
world.Export("[servers[I]]?[list2params(message)]")
/proc/ircadminwho()
var/list/message = list("Admins: ")
var/list/admin_keys = list()
for(var/adm in GLOB.admins)
var/client/C = adm
admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]"
for(var/admin in admin_keys)
if(LAZYLEN(message) > 1)
message += ", [admin]"
else
message += "[admin]"
return jointext(message, "")
/proc/keywords_lookup(msg,irc)
//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")
//explode the input msg into a list
var/list/msglist = splittext(msg, " ")
//generate keywords lookup
var/list/surnames = list()
var/list/forenames = list()
var/list/ckeys = list()
var/founds = ""
for(var/mob/M in GLOB.mob_list)
var/list/indexing = list(M.real_name, M.name)
if(M.mind)
indexing += M.mind.name
for(var/string in indexing)
var/list/L = splittext(string, " ")
var/surname_found = 0
//surnames
for(var/i=L.len, i>=1, i--)
var/word = ckey(L[i])
if(word)
surnames[word] = M
surname_found = i
break
//forenames
for(var/i=1, i<surname_found, i++)
var/word = ckey(L[i])
if(word)
forenames[word] = M
//ckeys
ckeys[M.ckey] = M
var/ai_found = 0
msg = ""
var/list/mobs_found = list()
for(var/original_word in msglist)
var/word = ckey(original_word)
if(word)
if(!(word in adminhelp_ignored_words))
if(word == "ai")
ai_found = 1
else
var/mob/found = ckeys[word]
if(!found)
found = surnames[word]
if(!found)
found = forenames[word]
if(found)
if(!(found in mobs_found))
mobs_found += found
if(!ai_found && isAI(found))
ai_found = 1
var/is_antag = 0
if(found.mind && found.mind.special_role)
is_antag = 1
founds += "Name: [found.name]([found.real_name]) Key: [found.key] Ckey: [found.ckey] [is_antag ? "(Antag)" : null] "
msg += "[original_word]<font size='1' color='[is_antag ? "red" : "black"]'>(<A HREF='?_src_=holder;[HrefToken(TRUE)];adminmoreinfo=[REF(found)]'>?</A>|<A HREF='?_src_=holder;[HrefToken(TRUE)];adminplayerobservefollow=[REF(found)]'>F</A>)</font> "
continue
msg += "[original_word] "
if(irc)
if(founds == "")
return "Search Failed"
else
return founds
return msg
-97
View File
@@ -1,97 +0,0 @@
/proc/show_air_status_to(turf/target, mob/user)
var/datum/gas_mixture/env = target.return_air()
var/list/env_gases = env.gases
var/burning = FALSE
if(isopenturf(target))
var/turf/open/T = target
if(T.active_hotspot)
burning = TRUE
var/list/lines = list("<span class='adminnotice'>[AREACOORD(target)]: [env.temperature] K ([env.temperature - T0C] C), [env.return_pressure()] kPa[(burning)?(", <font color='red'>burning</font>"):(null)]</span>")
for(var/id in env_gases)
var/moles = env_gases[id]
if (moles >= 0.00001)
lines += "[GLOB.meta_gas_names[id]]: [moles] mol"
to_chat(usr, lines.Join("\n"))
/client/proc/air_status(turf/target)
set category = "Debug"
set name = "Display Air Status"
if(!isturf(target))
return
show_air_status_to(target, usr)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Air Status") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/fix_next_move()
set category = "Debug"
set name = "Unfreeze Everyone"
var/largest_move_time = 0
var/largest_click_time = 0
var/mob/largest_move_mob = null
var/mob/largest_click_mob = null
for(var/mob/M in world)
if(!M.client)
continue
if(M.next_move >= largest_move_time)
largest_move_mob = M
if(M.next_move > world.time)
largest_move_time = M.next_move - world.time
else
largest_move_time = 1
if(M.next_click >= largest_click_time)
largest_click_mob = M
if(M.next_click > world.time)
largest_click_time = M.next_click - world.time
else
largest_click_time = 0
log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] lastDblClick = [M.next_click] world.time = [world.time]")
M.next_move = 1
M.next_click = 0
message_admins("[ADMIN_LOOKUPFLW(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [DisplayTimeText(largest_move_time)]!")
message_admins("[ADMIN_LOOKUPFLW(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [DisplayTimeText(largest_click_time)]!")
message_admins("world.time = [world.time]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Unfreeze Everyone") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/radio_report()
set category = "Debug"
set name = "Radio report"
var/output = "<b>Radio Report</b><hr>"
for (var/fq in SSradio.frequencies)
output += "<b>Freq: [fq]</b><br>"
var/datum/radio_frequency/fqs = SSradio.frequencies[fq]
if (!fqs)
output += "&nbsp;&nbsp;<b>ERROR</b><br>"
continue
for (var/filter in fqs.devices)
var/list/f = fqs.devices[filter]
if (!f)
output += "&nbsp;&nbsp;[filter]: ERROR<br>"
continue
output += "&nbsp;&nbsp;[filter]: [f.len]<br>"
for (var/device in f)
if (istype(device, /atom))
var/atom/A = device
output += "&nbsp;&nbsp;&nbsp;&nbsp;[device] ([AREACOORD(A)])<br>"
else
output += "&nbsp;&nbsp;&nbsp;&nbsp;[device]<br>"
usr << browse(output,"window=radioreport")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Radio Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/reload_admins()
set name = "Reload Admins"
set category = "Admin"
if(!src.holder)
return
var/confirm = alert(src, "Are you sure you want to reload all admins?", "Confirm", "Yes", "No")
if(confirm !="Yes")
return
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")
-358
View File
@@ -1,358 +0,0 @@
//- Are all the floors with or without air, as they should be? (regular or airless)
//- Does the area have an APC?
//- Does the area have an Air Alarm?
//- Does the area have a Request Console?
//- Does the area have lights?
//- Does the area have a light switch?
//- Does the area have enough intercoms?
//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug)
//- Is the area connected to the scrubbers air loop?
//- Is the area connected to the vent air loop? (vent pumps)
//- Is everything wired properly?
//- Does the area have a fire alarm and firedoors?
//- Do all pod doors work properly?
//- Are accesses set properly on doors, pod buttons, etc.
//- Are all items placed properly? (not below vents, scrubbers, tables)
//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room?
//- Check for any misplaced or stacked piece of pipe (air and disposal)
//- Check for any misplaced or stacked piece of wire
//- Identify how hard it is to break into the area and where the weak points are
//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels.
GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/camera_view, //-errorage
/client/proc/sec_camera_report, //-errorage
/client/proc/intercom_view, //-errorage
/client/proc/air_status, //Air things
/client/proc/Cell, //More air things
/client/proc/atmosscan, //check plumbing
/client/proc/powerdebug, //check power
/client/proc/count_objects_on_z_level,
/client/proc/count_objects_all,
/client/proc/cmd_assume_direct_control, //-errorage
/client/proc/startSinglo,
/client/proc/set_server_fps, //allows you to set the ticklag.
/client/proc/cmd_admin_grantfullaccess,
/client/proc/cmd_admin_areatest_all,
/client/proc/cmd_admin_areatest_station,
/client/proc/cmd_admin_test_atmos_controllers,
/client/proc/cmd_admin_rejuvenate,
/datum/admins/proc/show_traitor_panel,
/client/proc/disable_communication,
/client/proc/cmd_show_at_list,
/client/proc/cmd_show_at_markers,
/client/proc/manipulate_organs,
/client/proc/start_line_profiling,
/client/proc/stop_line_profiling,
/client/proc/show_line_profiling,
/client/proc/create_mapping_job_icons,
/client/proc/debug_z_levels,
/client/proc/place_ruin
))
GLOBAL_PROTECT(admin_verbs_debug_mapping)
/obj/effect/debugging/mapfix_marker
name = "map fix marker"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "mapfixmarker"
desc = "I am a mappers mistake."
/obj/effect/debugging/marker
icon = 'icons/turf/areas.dmi'
icon_state = "yellow"
/obj/effect/debugging/marker/Move()
return 0
/client/proc/camera_view()
set category = "Mapping"
set name = "Camera Range Display"
var/on = FALSE
for(var/turf/T in world)
if(T.maptext)
on = TRUE
T.maptext = null
if(!on)
var/list/seen = list()
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
for(var/turf/T in C.can_see())
seen[T]++
for(var/turf/T in seen)
T.maptext = "[seen[T]]"
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
/client/proc/sec_camera_report()
set category = "Mapping"
set name = "Camera Report"
if(!Master)
alert(usr,"Master_controller not found.","Sec Camera Report")
return 0
var/list/obj/machinery/camera/CL = list()
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
CL += C
var/output = {"<B>Camera Abnormalities Report</B><HR>
<B>The following abnormalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.</B><BR><ul>"}
for(var/obj/machinery/camera/C1 in CL)
for(var/obj/machinery/camera/C2 in CL)
if(C1 != C2)
if(C1.c_tag == C2.c_tag)
output += "<li><font color='red'>c_tag match for cameras at [ADMIN_VERBOSEJMP(C1)] and [ADMIN_VERBOSEJMP(C2)] - c_tag is [C1.c_tag]</font></li>"
if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y)
output += "<li><font color='red'>FULLY overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</font></li>"
if(C1.loc == C2.loc)
output += "<li>Overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</li>"
var/turf/T = get_step(C1,turn(C1.dir,180))
if(!T || !isturf(T) || !T.density )
if(!(locate(/obj/structure/grille) in T))
var/window_check = 0
for(var/obj/structure/window/W in T)
if (W.dir == turn(C1.dir,180) || W.dir in list(5,6,9,10) )
window_check = 1
break
if(!window_check)
output += "<li><font color='red'>Camera not connected to wall at [ADMIN_VERBOSEJMP(C1)] Network: [json_encode(C1.network)]</font></li>"
output += "</ul>"
usr << browse(output,"window=airreport;size=1000x500")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/intercom_view()
set category = "Mapping"
set name = "Intercom Range Display"
var/static/intercom_range_display_status = FALSE
intercom_range_display_status = !intercom_range_display_status //blame cyberboss if this breaks something
for(var/obj/effect/debugging/marker/M in world)
qdel(M)
if(intercom_range_display_status)
for(var/obj/item/radio/intercom/I in world)
for(var/turf/T in orange(7,I))
var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T)
if (!(F in view(7,I.loc)))
qdel(F)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Intercom Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_show_at_list()
set category = "Mapping"
set name = "Show roundstart AT list"
set desc = "Displays a list of active turfs coordinates at roundstart"
var/dat = {"<b>Coordinate list of Active Turfs at Roundstart</b>
<br>Real-time Active Turfs list you can see in Air Subsystem at active_turfs var<br>"}
for(var/t in GLOB.active_turfs_startlist)
var/turf/T = t
dat += "[ADMIN_VERBOSEJMP(T)]\n"
dat += "<br>"
usr << browse(dat, "window=at_list")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turfs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_show_at_markers()
set category = "Mapping"
set name = "Show roundstart AT markers"
set desc = "Places a marker on all active-at-roundstart turfs"
var/count = 0
for(var/obj/effect/abstract/marker/at/AT in GLOB.all_abstract_markers)
qdel(AT)
count++
if(count)
to_chat(usr, "[count] AT markers removed.")
else
for(var/t in GLOB.active_turfs_startlist)
new /obj/effect/abstract/marker/at(t)
count++
to_chat(usr, "[count] AT markers placed.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turf Markers")
/client/proc/enable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Enable"
if(!check_rights(R_DEBUG))
return
verbs -= /client/proc/enable_debug_verbs
verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/disable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Disable"
verbs.Remove(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
verbs += /client/proc/enable_debug_verbs
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_on_z_level()
set category = "Mapping"
set name = "Count Objects On Level"
var/level = input("Which z-level?","Level?") as text
if(!level)
return
var/num_level = text2num(level)
if(!num_level)
return
if(!isnum(num_level))
return
var/type_text = input("Which type path?","Path?") as text
if(!type_text)
return
var/type_path = text2path(type_text)
if(!type_path)
return
var/count = 0
var/list/atom/atom_list = list()
for(var/atom/A in world)
if(istype(A,type_path))
var/atom/B = A
while(!(isturf(B.loc)))
if(B && B.loc)
B = B.loc
else
break
if(B)
if(B.z == num_level)
count++
atom_list += A
to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects Zlevel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_all()
set category = "Mapping"
set name = "Count Objects All"
var/type_text = input("Which type path?","") as text
if(!type_text)
return
var/type_path = text2path(type_text)
if(!type_path)
return
var/count = 0
for(var/atom/A in world)
if(istype(A,type_path))
count++
to_chat(world, "There are [count] objects of type [type_path] in the game world")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects All") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//This proc is intended to detect lag problems relating to communication procs
GLOBAL_VAR_INIT(say_disabled, FALSE)
/client/proc/disable_communication()
set category = "Mapping"
set name = "Disable all communication verbs"
GLOB.say_disabled = !GLOB.say_disabled
if(GLOB.say_disabled)
message_admins("[key] used 'Disable all communication verbs', killing all communication methods.")
else
message_admins("[key] used 'Disable all communication verbs', restoring all communication methods.")
//This generates the icon states for job starting location landmarks.
/client/proc/create_mapping_job_icons()
set name = "Generate job landmarks icons"
set category = "Mapping"
var/icon/final = icon()
var/mob/living/carbon/human/dummy/D = new(locate(1,1,1)) //spawn on 1,1,1 so we don't have runtimes when items are deleted
D.setDir(SOUTH)
for(var/job in subtypesof(/datum/job))
var/datum/job/JB = new job
switch(JB.title)
if("AI")
final.Insert(icon('icons/mob/ai.dmi', "ai", SOUTH, 1), "AI")
if("Cyborg")
final.Insert(icon('icons/mob/robots.dmi', "robot", SOUTH, 1), "Cyborg")
else
for(var/obj/item/I in D)
qdel(I)
randomize_human(D)
JB.equip(D, TRUE, FALSE)
COMPILE_OVERLAYS(D)
var/icon/I = icon(getFlatIcon(D), frame = 1)
final.Insert(I, JB.title)
qdel(D)
//Also add the x
for(var/x_number in 1 to 4)
final.Insert(icon('icons/mob/screen_gen.dmi', "x[x_number == 1 ? "" : x_number]"), "x[x_number == 1 ? "" : x_number]")
fcopy(final, "icons/mob/landmarks.dmi")
/client/proc/debug_z_levels()
set name = "Debug Z-Levels"
set category = "Mapping"
var/list/z_list = SSmapping.z_list
var/list/messages = list()
messages += "<b>World</b>: [world.maxx] x [world.maxy] x [world.maxz]<br>"
var/list/linked_levels = list()
var/min_x = INFINITY
var/min_y = INFINITY
var/max_x = -INFINITY
var/max_y = -INFINITY
for(var/z in 1 to max(world.maxz, z_list.len))
if (z > z_list.len)
messages += "<b>[z]</b>: Unmanaged (out of bounds)<br>"
continue
var/datum/space_level/S = z_list[z]
if (!S)
messages += "<b>[z]</b>: Unmanaged (null)<br>"
continue
var/linkage
switch (S.linkage)
if (UNAFFECTED)
linkage = "no linkage"
if (SELFLOOPING)
linkage = "self-looping"
if (CROSSLINKED)
linkage = "linked at ([S.xi], [S.yi])"
linked_levels += S
min_x = min(min_x, S.xi)
min_y = min(min_y, S.yi)
max_x = max(max_x, S.xi)
max_y = max(max_y, S.yi)
else
linkage = "unknown linkage '[S.linkage]'"
messages += "<b>[z]</b>: [S.name], [linkage], traits: [json_encode(S.traits)]<br>"
if (S.z_value != z)
messages += "-- z_value is [S.z_value], should be [z]<br>"
if (S.name == initial(S.name))
messages += "-- name not set<br>"
if (z > world.maxz)
messages += "-- exceeds max z"
var/grid[max_x - min_x + 1][max_y - min_y + 1]
for(var/datum/space_level/S in linked_levels)
grid[S.xi - min_x + 1][S.yi - min_y + 1] = S.z_value
messages += "<table border='1'>"
for(var/y in max_y to min_y step -1)
var/list/part = list()
for(var/x in min_x to max_x)
part += "[grid[x - min_x + 1][y - min_y + 1]]"
messages += "<tr><td>[part.Join("</td><td>")]</td></tr>"
messages += "</table>"
to_chat(src, messages.Join(""))
-644
View File
@@ -1,644 +0,0 @@
GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "force_ending"))
GLOBAL_PROTECT(VVlocked)
GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize"))
GLOBAL_PROTECT(VVicon_edit_lock)
GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey"))
GLOBAL_PROTECT(VVckey_edit)
GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y"))
GLOBAL_PROTECT(VVpixelmovement)
/client/proc/vv_get_class(var/var_name, var/var_value)
if(isnull(var_value))
. = VV_NULL
else if (isnum(var_value))
if (var_name in GLOB.bitfields)
. = VV_BITFIELD
else
. = VV_NUM
else if (istext(var_value))
if (findtext(var_value, "\n"))
. = VV_MESSAGE
else
. = VV_TEXT
else if (isicon(var_value))
. = VV_ICON
else if (ismob(var_value))
. = VV_MOB_REFERENCE
else if (isloc(var_value))
. = VV_ATOM_REFERENCE
else if (istype(var_value, /client))
. = VV_CLIENT
else if (istype(var_value, /datum))
. = VV_DATUM_REFERENCE
else if (ispath(var_value))
if (ispath(var_value, /atom))
. = VV_ATOM_TYPE
else if (ispath(var_value, /datum))
. = VV_DATUM_TYPE
else
. = VV_TYPE
else if (islist(var_value))
. = VV_LIST
else if (isfile(var_value))
. = VV_FILE
else
. = VV_NULL
/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name)
. = list("class" = class, "value" = null)
if (!class)
if (!classes)
classes = list (
VV_NUM,
VV_TEXT,
VV_MESSAGE,
VV_ICON,
VV_ATOM_REFERENCE,
VV_DATUM_REFERENCE,
VV_MOB_REFERENCE,
VV_CLIENT,
VV_ATOM_TYPE,
VV_DATUM_TYPE,
VV_TYPE,
VV_FILE,
VV_NEW_ATOM,
VV_NEW_DATUM,
VV_NEW_TYPE,
VV_NEW_LIST,
VV_NULL,
VV_RESTORE_DEFAULT
)
if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes))
classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])"
if (restricted_classes)
classes -= restricted_classes
if (extra_classes)
classes += extra_classes
.["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes
if (holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])")
.["class"] = VV_MARKED_DATUM
switch(.["class"])
if (VV_TEXT)
.["value"] = input("Enter new text:", "Text", current_value) as null|text
if (.["value"] == null)
.["class"] = null
return
if (VV_MESSAGE)
.["value"] = input("Enter new text:", "Text", current_value) as null|message
if (.["value"] == null)
.["class"] = null
return
if (VV_NUM)
.["value"] = input("Enter new number:", "Num", current_value) as null|num
if (.["value"] == null)
.["class"] = null
return
if (VV_BITFIELD)
.["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value)
if (.["value"] == null)
.["class"] = null
return
if (VV_ATOM_TYPE)
.["value"] = pick_closest_path(FALSE)
if (.["value"] == null)
.["class"] = null
return
if (VV_DATUM_TYPE)
.["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
if (.["value"] == null)
.["class"] = null
return
if (VV_TYPE)
var/type = current_value
var/error = ""
do
type = input("Enter type:[error]", "Type", type) as null|text
if (!type)
break
type = text2path(type)
error = "\nType not found, Please try again"
while(!type)
if (!type)
.["class"] = null
return
.["value"] = type
if (VV_ATOM_REFERENCE)
var/type = pick_closest_path(FALSE)
var/subtypes = vv_subtype_prompt(type)
if (subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = input("Select reference:", "Reference", current_value) as null|anything in things
if (!value)
.["class"] = null
return
.["value"] = things[value]
if (VV_DATUM_REFERENCE)
var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
var/subtypes = vv_subtype_prompt(type)
if (subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = input("Select reference:", "Reference", current_value) as null|anything in things
if (!value)
.["class"] = null
return
.["value"] = things[value]
if (VV_MOB_REFERENCE)
var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob)))
var/subtypes = vv_subtype_prompt(type)
if (subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = input("Select reference:", "Reference", current_value) as null|anything in things
if (!value)
.["class"] = null
return
.["value"] = things[value]
if (VV_CLIENT)
.["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients
if (.["value"] == null)
.["class"] = null
return
if (VV_FILE)
.["value"] = input("Pick file:", "File") as null|file
if (.["value"] == null)
.["class"] = null
return
if (VV_ICON)
.["value"] = input("Pick icon:", "Icon") as null|icon
if (.["value"] == null)
.["class"] = null
return
if (VV_MARKED_DATUM)
.["value"] = holder.marked_datum
if (.["value"] == null)
.["class"] = null
return
if (VV_NEW_ATOM)
var/type = pick_closest_path(FALSE)
if (!type)
.["class"] = null
return
.["type"] = type
var/atom/newguy = new type()
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
if (VV_NEW_DATUM)
var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
if (!type)
.["class"] = null
return
.["type"] = type
var/datum/newguy = new type()
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
if (VV_NEW_TYPE)
var/type = current_value
var/error = ""
do
type = input("Enter type:[error]", "Type", type) as null|text
if (!type)
break
type = text2path(type)
error = "\nType not found, Please try again"
while(!type)
if (!type)
.["class"] = null
return
.["type"] = type
var/datum/newguy = new type()
if(istype(newguy))
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
if (VV_NEW_LIST)
.["value"] = list()
.["type"] = /list
/client/proc/vv_parse_text(O, new_var)
if(O && findtext(new_var,"\["))
var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No")
if(process_vars == "Yes")
. = string2listofvars(new_var, O)
//do they want you to include subtypes?
//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes)
//TRUE = Yes subtypes
//NULL = User cancelled at the prompt or invalid type given
/client/proc/vv_subtype_prompt(var/type)
if (!ispath(type))
return
var/list/subtypes = subtypesof(type)
if (!subtypes || !subtypes.len)
return FALSE
if (subtypes && subtypes.len)
switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel"))
if("Strictly this type")
return FALSE
if("This type and subtypes")
return TRUE
else
return
/client/proc/vv_reference_list(type, subtypes)
. = list()
var/list/types = list(type)
if (subtypes)
types = typesof(type)
var/list/fancytypes = make_types_fancy(types)
for(var/fancytype in fancytypes) //swap the assoication
types[fancytypes[fancytype]] = fancytype
var/things = get_all_of_type(type, subtypes)
var/i = 0
for(var/thing in things)
var/datum/D = thing
i++
//try one of 3 methods to shorten the type text:
// fancy type,
// fancy type with the base type removed from the begaining,
// the type with the base type removed from the begaining
var/fancytype = types[D.type]
if (findtext(fancytype, types[type]))
fancytype = copytext(fancytype, length(types[type])+1)
var/shorttype = copytext("[D.type]", length("[type]")+1)
if (length(shorttype) > length(fancytype))
shorttype = fancytype
if (!length(shorttype))
shorttype = "/"
.["[D]([shorttype])[REF(D)]#[i]"] = D
/client/proc/mod_list_add_ass(atom/O) //hehe
var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = L["class"]
if (!class)
return
var/var_value = L["value"]
if(class == VV_TEXT || class == VV_MESSAGE)
var/list/varsvars = vv_parse_text(O, var_value)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
return var_value
/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar)
var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = LL["class"]
if (!class)
return
var/var_value = LL["value"]
if(class == VV_TEXT || class == VV_MESSAGE)
var/list/varsvars = vv_parse_text(O, var_value)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
if (O)
L = L.Copy()
L += var_value
switch(alert("Would you like to associate a value with the list entry?",,"Yes","No"))
if("Yes")
L[var_value] = mod_list_add_ass(O) //hehe
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE)
if(!check_rights(R_VAREDIT))
return
if(!istype(L, /list))
to_chat(src, "Not a List.")
return
if(L.len > 1000)
var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort")
if(confirm != "Continue")
return
var/list/names = list()
for (var/i in 1 to L.len)
var/key = L[i]
var/value
if (IS_NORMAL_LIST(L) && !isnum(key))
value = L[key]
if (value == null)
value = "null"
names["#[i] [key] = [value]"] = i
if (!index)
var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)"
if(variable == null)
return
if(variable == "(ADD VAR)")
mod_list_add(L, O, original_name, objectvar)
return
if(variable == "(CLEAR NULLS)")
L = L.Copy()
listclearnulls(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS")
return
if(variable == "(CLEAR DUPES)")
L = uniqueList(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES")
return
if(variable == "(SHUFFLE)")
L = shuffle(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE")
return
index = names[variable]
var/assoc_key
if (index == null)
return
var/assoc = 0
var/prompt = alert(src, "Do you want to edit the key or its assigned value?", "Associated List", "Key", "Assigned Value", "Cancel")
if (prompt == "Cancel")
return
if (prompt == "Assigned Value")
assoc = 1
assoc_key = L[index]
var/default
var/variable
if (assoc)
variable = L[assoc_key]
else
variable = L[index]
default = vv_get_class(objectvar, variable)
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.")
to_chat(src, "Variable contains: [variable]")
if(default == VV_NUM)
var/dir_text = ""
var/tdir = variable
if(tdir > 0 && tdir < 16)
if(tdir & 1)
dir_text += "NORTH"
if(tdir & 2)
dir_text += "SOUTH"
if(tdir & 4)
dir_text += "EAST"
if(tdir & 8)
dir_text += "WEST"
if(dir_text)
to_chat(usr, "If a direction, direction is: [dir_text]")
var/original_var = variable
if (O)
L = L.Copy()
var/class
if(autodetect_class)
if (default == VV_TEXT)
default = VV_MESSAGE
class = default
var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST"))
class = LL["class"]
if (!class)
return
var/new_var = LL["value"]
if(class == VV_MESSAGE)
class = VV_TEXT
switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet.
if(VV_LIST)
mod_list(variable, O, original_name, objectvar)
if("DELETE FROM LIST")
L.Cut(index, index+1)
if (O)
if (O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
return
if(VV_TEXT)
var/list/varsvars = vv_parse_text(O, new_var)
for(var/V in varsvars)
new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]")
if(assoc)
L[assoc_key] = new_var
else
L[index] = new_var
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
to_chat(src, "Your edit was rejected by the object.")
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]")
/proc/vv_varname_lockcheck(param_var_name)
if(param_var_name in GLOB.VVlocked)
if(!check_rights(R_DEBUG))
return FALSE
if(param_var_name in GLOB.VVckey_edit)
if(!check_rights(R_SPAWN|R_DEBUG))
return FALSE
if(param_var_name in GLOB.VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return FALSE
if(param_var_name in GLOB.VVpixelmovement)
if(!check_rights(R_DEBUG))
return FALSE
var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT")
if (prompt != "Continue")
return FALSE
return TRUE
/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0)
if(!check_rights(R_VAREDIT))
return
var/class
var/variable
var/var_value
if(param_var_name)
if(!(param_var_name in O.vars))
to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])")
return
variable = param_var_name
else
var/list/names = list()
for (var/V in O.vars)
names += V
names = sortList(names)
variable = input("Which var?","Var") as null|anything in names
if(!variable)
return
if(!O.can_vv_get(variable))
return
var_value = O.vars[variable]
if(!vv_varname_lockcheck(variable))
return
if(istype(O, /datum/armor))
var/prompt = alert(src, "Editing this var changes this value on potentially thousands of items that share the same combination of armor values. If you want to edit the armor of just one item, use the \"Modify armor values\" dropdown item", "DANGER", "ABORT ", "Continue", " ABORT")
if (prompt != "Continue")
return
var/default = vv_get_class(variable, var_value)
if(isnull(default))
to_chat(src, "Unable to determine variable type.")
else
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.")
to_chat(src, "Variable contains: [var_value]")
if(default == VV_NUM)
var/dir_text = ""
if(var_value > 0 && var_value < 16)
if(var_value & 1)
dir_text += "NORTH"
if(var_value & 2)
dir_text += "SOUTH"
if(var_value & 4)
dir_text += "EAST"
if(var_value & 8)
dir_text += "WEST"
if(dir_text)
to_chat(src, "If a direction, direction is: [dir_text]")
if(autodetect_class && default != VV_NULL)
if (default == VV_TEXT)
default = VV_MESSAGE
class = default
var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST), var_name = variable)
class = value["class"]
if (!class)
return
var/var_new = value["value"]
if(class == VV_MESSAGE)
class = VV_TEXT
var/original_name = "[O]"
switch(class)
if(VV_LIST)
if(!islist(var_value))
mod_list(list(), O, original_name, variable)
mod_list(var_value, O, original_name, variable)
return
if(VV_RESTORE_DEFAULT)
var_new = initial(O.vars[variable])
if(VV_TEXT)
var/list/varsvars = vv_parse_text(O, var_new)
for(var/V in varsvars)
var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]")
if (O.vv_edit_var(variable, var_new) == FALSE)
to_chat(src, "Your edit was rejected by the object.")
return
vv_update_display(O, "varedited", VV_MSG_EDITED)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args)
log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]")
var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]"
message_admins(msg)
admin_ticket_log(O, msg)
return TRUE
File diff suppressed because it is too large Load Diff
-70
View File
@@ -1,70 +0,0 @@
/datum/admins/proc/spawn_objasmob(object as text)
set category = "Debug"
set desc = "(obj path) Spawn object-mob"
set name = "Spawn object-mob"
if(!check_rights(R_SPAWN))
return
var/chosen = pick_closest_path(object, make_types_fancy(subtypesof(/obj)))
if (!chosen)
return
var/mob/living/simple_animal/hostile/mimic/copy/basemob = /mob/living/simple_animal/hostile/mimic/copy
var/obj/chosen_obj = text2path(chosen)
var/list/settings = list(
"mainsettings" = list(
"name" = list("desc" = "Name", "type" = "string", "value" = "Bob"),
"maxhealth" = list("desc" = "Max. health", "type" = "number", "value" = 100),
"access" = list("desc" = "Access ID", "type" = "datum", "path" = "/obj/item/card/id", "value" = "Default"),
"objtype" = list("desc" = "Base obj type", "type" = "datum", "path" = "/obj", "value" = "[chosen]"),
"googlyeyes" = list("desc" = "Googly eyes", "type" = "boolean", "value" = "No"),
"disableai" = list("desc" = "Disable AI", "type" = "boolean", "value" = "Yes"),
"idledamage" = list("desc" = "Damaged while idle", "type" = "boolean", "value" = "No"),
"dropitem" = list("desc" = "Drop obj on death", "type" = "boolean", "value" = "Yes"),
"mobtype" = list("desc" = "Base mob type", "type" = "datum", "path" = "/mob/living/simple_animal/hostile/mimic/copy", "value" = "/mob/living/simple_animal/hostile/mimic/copy"),
"ckey" = list("desc" = "ckey", "type" = "ckey", "value" = "none"),
)
)
var/list/prefreturn = presentpreflikepicker(usr,"Customize mob", "Customize mob", Button1="Ok", width = 450, StealFocus = 1,Timeout = 0, settings=settings)
if (prefreturn["button"] == 1)
settings = prefreturn["settings"]
var/mainsettings = settings["mainsettings"]
chosen_obj = text2path(mainsettings["objtype"]["value"])
basemob = text2path(mainsettings["mobtype"]["value"])
if (!ispath(basemob, /mob/living/simple_animal/hostile/mimic/copy) || !ispath(chosen_obj, /obj))
to_chat(usr, "Mob or object path invalid")
basemob = new basemob(get_turf(usr), new chosen_obj(get_turf(usr)), usr, mainsettings["dropitem"]["value"] == "Yes" ? FALSE : TRUE, (mainsettings["googlyeyes"]["value"] == "Yes" ? FALSE : TRUE))
if (mainsettings["disableai"]["value"] == "Yes")
basemob.toggle_ai(AI_OFF)
if (mainsettings["idledamage"]["value"] == "No")
basemob.idledamage = FALSE
if (mainsettings["access"])
var/newaccess = text2path(mainsettings["access"]["value"])
if (ispath(newaccess))
basemob.access_card = new newaccess
if (mainsettings["maxhealth"]["value"])
if (!isnum(mainsettings["maxhealth"]["value"]))
mainsettings["maxhealth"]["value"] = text2num(mainsettings["maxhealth"]["value"])
if (mainsettings["maxhealth"]["value"] > 0)
basemob.maxHealth = basemob.maxHealth = mainsettings["maxhealth"]["value"]
if (mainsettings["name"]["value"])
basemob.name = basemob.real_name = html_decode(mainsettings["name"]["value"])
if (mainsettings["ckey"]["value"] != "none")
basemob.ckey = mainsettings["ckey"]["value"]
log_admin("[key_name(usr)] spawned a sentient object-mob [basemob] from [chosen_obj] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn object-mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -1,229 +0,0 @@
GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist
var/name = "Antagonist"
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
var/datum/mind/owner //Mind that owns this datum
var/silent = FALSE //Silent will prevent the gain/lose texts to show
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
var/delete_on_mind_deletion = TRUE
var/job_rank
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
/datum/antagonist/New()
GLOB.antagonists += src
typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
/datum/antagonist/Destroy()
GLOB.antagonists -= src
if(owner)
LAZYREMOVE(owner.antag_datums, src)
owner = null
return ..()
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
. = TRUE
var/datum/mind/tested = new_owner || owner
if(tested.has_antag_datum(type))
return FALSE
for(var/i in tested.antag_datums)
var/datum/antagonist/A = i
if(is_type_in_typecache(src, A.typecache_datum_blacklist))
return FALSE
//This will be called in add_antag_datum before owner assignment.
//Should return antag datum without owner.
/datum/antagonist/proc/specialization(datum/mind/new_owner)
return src
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
remove_innate_effects(old_body)
apply_innate_effects(new_body)
//This handles the application of antag huds/special abilities
/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
return
//This handles the removal of antag huds/special abilities
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
return
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
//Proc called when the datum is given to a mind.
/datum/antagonist/proc/on_gain()
if(owner && owner.current)
if(!silent)
greet()
apply_innate_effects()
give_antag_moodies()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
. = (jobban_isbanned(M, ROLE_SYNDICATE) || QDELETED(M) || (job_rank && (jobban_isbanned(M,job_rank) || QDELETED(M))))
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
owner.current.ghostize(0)
C.transfer_ckey(owner.current, FALSE)
/datum/antagonist/proc/on_removal()
remove_innate_effects()
clear_antag_moodies()
if(owner)
LAZYREMOVE(owner.antag_datums, src)
if(!silent && owner.current)
farewell()
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
qdel(src)
/datum/antagonist/proc/greet()
return
/datum/antagonist/proc/farewell()
return
/datum/antagonist/proc/give_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "antag_moodlet", antag_moodlet)
/datum/antagonist/proc/clear_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
//Returns the team antagonist belongs to if any.
/datum/antagonist/proc/get_team()
return
//Individual roundend report
/datum/antagonist/proc/roundend_report()
var/list/report = list()
if(!owner)
CRASH("antagonist datum without owner")
report += printplayer(owner)
var/objectives_complete = TRUE
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
objectives_complete = FALSE
break
if(objectives.len == 0 || objectives_complete)
report += "<span class='greentext big'>The [name] was successful!</span>"
else
report += "<span class='redtext big'>The [name] has failed!</span>"
return report.Join("<br>")
//Displayed at the start of roundend_category section, default to roundend_category header
/datum/antagonist/proc/roundend_report_header()
return "<span class='header'>The [roundend_category] were:</span><br>"
//Displayed at the end of roundend_category section
/datum/antagonist/proc/roundend_report_footer()
return
//ADMIN TOOLS
//Called when using admin tools to give antag status
/datum/antagonist/proc/admin_add(datum/mind/new_owner,mob/admin)
message_admins("[key_name_admin(admin)] made [new_owner.current] into [name].")
log_admin("[key_name(admin)] made [new_owner.current] into [name].")
new_owner.add_antag_datum(src)
//Called when removing antagonist using admin tools
/datum/antagonist/proc/admin_remove(mob/user)
if(!user)
return
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [owner.current].")
log_admin("[key_name(user)] has removed [name] antagonist status from [owner.current].")
on_removal()
//gamemode/proc/is_mode_antag(antagonist/A) => TRUE/FALSE
//Additional data to display in antagonist panel section
//nuke disk code, genome count, etc
/datum/antagonist/proc/antag_panel_data()
return ""
/datum/antagonist/proc/enabled_in_preferences(datum/mind/M)
if(job_rank)
if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special))
return TRUE
else
return FALSE
return TRUE
// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/datum/antagonist/proc/get_admin_commands()
. = list()
/datum/antagonist/Topic(href,href_list)
if(!check_rights(R_ADMIN))
return
//Antag memory edit
if (href_list["memory_edit"])
edit_memory(usr)
owner.traitor_panel()
return
//Some commands might delete/modify this datum clearing or changing owner
var/datum/mind/persistent_owner = owner
var/commands = get_admin_commands()
for(var/admin_command in commands)
if(href_list["command"] == admin_command)
var/datum/callback/C = commands[admin_command]
C.Invoke(usr)
persistent_owner.traitor_panel()
return
/datum/antagonist/proc/edit_memory(mob/user)
var/new_memo = copytext(trim(input(user,"Write new memory", "Memory", antag_memory) as null|message),1,MAX_MESSAGE_LEN)
if (isnull(new_memo))
return
antag_memory = new_memo
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
antagpanel_category = "Custom"
show_name_in_check_antagonists = TRUE //They're all different
/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
if(custom_name)
name = custom_name
else
return
..()
@@ -1,280 +0,0 @@
/obj/item/antag_spawner
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_TINY
var/used = FALSE
/obj/item/antag_spawner/proc/spawn_antag(client/C, turf/T, kind = "", datum/mind/user)
return
/obj/item/antag_spawner/proc/equip_antag(mob/target)
return
///////////WIZARD
/obj/item/antag_spawner/contract
name = "contract"
desc = "A magic contract previously signed by an apprentice. In exchange for instruction in the magical arts, they are bound to answer your call for aid."
icon = 'icons/obj/wizard.dmi'
icon_state ="scroll2"
/obj/item/antag_spawner/contract/attack_self(mob/user)
user.set_machine(src)
var/dat
if(used)
dat = "<B>You have already summoned your apprentice.</B><BR>"
else
dat = "<B>Contract of Apprenticeship:</B><BR>"
dat += "<I>Using this contract, you may summon an apprentice to aid you on your mission.</I><BR>"
dat += "<I>If you are unable to establish contact with your apprentice, you can feed the contract back to the spellbook to refund your points.</I><BR>"
dat += "<B>Which school of magic is your apprentice studying?:</B><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_DESTRUCTION]'>Destruction</A><BR>"
dat += "<I>Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_BLUESPACE]'>Bluespace Manipulation</A><BR>"
dat += "<I>Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_HEALING]'>Healing</A><BR>"
dat += "<I>Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_ROBELESS]'>Robeless</A><BR>"
dat += "<I>Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.</I><BR>"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/antag_spawner/contract/Topic(href, href_list)
..()
var/mob/living/carbon/human/H = usr
if(H.stat || H.restrained())
return
if(!ishuman(H))
return 1
if(loc == H || (in_range(src, H) && isturf(loc)))
H.set_machine(src)
if(href_list["school"])
if(used)
to_chat(H, "You already used this contract!")
return
var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, null, ROLE_WIZARD, 150, src)
if(LAZYLEN(candidates))
if(QDELETED(src))
return
if(used)
to_chat(H, "You already used this contract!")
return
used = TRUE
var/mob/dead/observer/C = pick(candidates)
spawn_antag(C.client, get_turf(src), href_list["school"],H.mind)
else
to_chat(H, "Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later.")
/obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind ,datum/mind/user)
new /obj/effect/particle_effect/smoke(T)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/datum/mind/app_mind = M.mind
var/datum/antagonist/wizard/apprentice/app = new()
app.master = user
app.school = kind
var/datum/antagonist/wizard/master_wizard = user.has_antag_datum(/datum/antagonist/wizard)
if(master_wizard)
if(!master_wizard.wiz_team)
master_wizard.create_wiz_team()
app.wiz_team = master_wizard.wiz_team
master_wizard.wiz_team.add_member(app_mind)
app_mind.add_antag_datum(app)
//TODO Kill these if possible
app_mind.assigned_role = "Apprentice"
app_mind.special_role = "apprentice"
//
SEND_SOUND(M, sound('sound/effects/magic.ogg'))
///////////BORGS AND OPERATIVES
/obj/item/antag_spawner/nuke_ops
name = "syndicate operative teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/borg_to_spawn
/obj/item/antag_spawner/nuke_ops/proc/check_usability(mob/user)
if(used)
to_chat(user, "<span class='warning'>[src] is out of power!</span>")
return FALSE
if(!user.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE))
to_chat(user, "<span class='danger'>AUTHENTICATION FAILURE. ACCESS DENIED.</span>")
return FALSE
if(!user.onSyndieBase())
to_chat(user, "<span class='warning'>[src] is out of range! It can only be used at your base!</span>")
return FALSE
return TRUE
/obj/item/antag_spawner/nuke_ops/attack_self(mob/user)
if(!(check_usability(user)))
return
to_chat(user, "<span class='notice'>You activate [src] and wait for confirmation.</span>")
var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE)
if(LAZYLEN(nuke_candidates))
if(QDELETED(src) || !check_usability(user))
return
used = TRUE
var/mob/dead/observer/G = pick(nuke_candidates)
spawn_antag(G.client, get_turf(src), "syndieborg", user.mind)
do_sparks(4, TRUE, src)
qdel(src)
else
to_chat(user, "<span class='warning'>Unable to connect to Syndicate command. Please wait and try again later or use the teleporter on your uplink to get your points refunded.</span>")
/obj/item/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/datum/antagonist/nukeop/new_op = new()
new_op.send_to_spawnpoint = FALSE
new_op.nukeop_outfit = /datum/outfit/syndicate/no_crystals
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop,TRUE)
if(creator_op)
M.mind.add_antag_datum(new_op,creator_op.nuke_team)
M.mind.special_role = "Nuclear Operative"
//////CLOWN OP
/obj/item/antag_spawner/nuke_ops/clown
name = "clown operative teleporter"
desc = "A single-use teleporter designed to quickly reinforce clown operatives in the field."
/obj/item/antag_spawner/nuke_ops/clown/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/datum/antagonist/nukeop/clownop/new_op = new /datum/antagonist/nukeop/clownop()
new_op.send_to_spawnpoint = FALSE
new_op.nukeop_outfit = /datum/outfit/syndicate/clownop/no_crystals
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop/clownop,TRUE)
if(creator_op)
M.mind.add_antag_datum(new_op, creator_op.nuke_team)
M.mind.special_role = "Clown Operative"
//////SYNDICATE BORG
/obj/item/antag_spawner/nuke_ops/borg_tele
name = "syndicate cyborg teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
/obj/item/antag_spawner/nuke_ops/borg_tele/assault
name = "syndicate assault cyborg teleporter"
borg_to_spawn = "Assault"
/obj/item/antag_spawner/nuke_ops/borg_tele/medical
name = "syndicate medical teleporter"
borg_to_spawn = "Medical"
/obj/item/antag_spawner/nuke_ops/borg_tele/saboteur
name = "syndicate saboteur teleporter"
borg_to_spawn = "Saboteur"
/obj/item/antag_spawner/nuke_ops/borg_tele/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/silicon/robot/R
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop,TRUE)
if(!creator_op)
return
switch(borg_to_spawn)
if("Medical")
R = new /mob/living/silicon/robot/modules/syndicate/medical(T)
if("Saboteur")
R = new /mob/living/silicon/robot/modules/syndicate/saboteur(T)
else
R = new /mob/living/silicon/robot/modules/syndicate(T) //Assault borg by default
var/brainfirstname = pick(GLOB.first_names_male)
if(prob(50))
brainfirstname = pick(GLOB.first_names_female)
var/brainopslastname = pick(GLOB.last_names)
if(creator_op.nuke_team.syndicate_name) //the brain inside the syndiborg has the same last name as the other ops.
brainopslastname = creator_op.nuke_team.syndicate_name
var/brainopsname = "[brainfirstname] [brainopslastname]"
R.mmi.name = "Man-Machine Interface: [brainopsname]"
R.mmi.brain.name = "[brainopsname]'s brain"
R.mmi.brainmob.real_name = brainopsname
R.mmi.brainmob.name = brainopsname
R.real_name = R.name
R.key = C.key
var/datum/antagonist/nukeop/new_borg = new()
new_borg.send_to_spawnpoint = FALSE
R.mind.add_antag_datum(new_borg,creator_op.nuke_team)
R.mind.special_role = "Syndicate Cyborg"
///////////SLAUGHTER DEMON
/obj/item/antag_spawner/slaughter_demon //Warning edgiest item in the game
name = "vial of blood"
desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
var/shatter_msg = "<span class='notice'>You shatter the bottle, no turning back now!</span>"
var/veil_msg = "<span class='warning'>You sense a dark presence lurking just beyond the veil...</span>"
var/mob/living/demon_type = /mob/living/simple_animal/slaughter
var/antag_type = /datum/antagonist/slaughter
/obj/item/antag_spawner/slaughter_demon/attack_self(mob/user)
if(!is_station_level(user.z))
to_chat(user, "<span class='notice'>You should probably wait until you reach the station.</span>")
return
if(used)
return
var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, src)
if(LAZYLEN(candidates))
if(used || QDELETED(src))
return
used = TRUE
var/mob/dead/observer/C = pick(candidates)
spawn_antag(C.client, get_turf(src), initial(demon_type.name),user.mind)
to_chat(user, shatter_msg)
to_chat(user, veil_msg)
playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, 1)
qdel(src)
else
to_chat(user, "<span class='notice'>You can't seem to work up the nerve to shatter the bottle. Perhaps you should try again later.</span>")
/obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, kind = "", datum/mind/user)
var/obj/effect/dummy/phased_mob/slaughter/holder = new /obj/effect/dummy/phased_mob/slaughter(T)
var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
S.holder = holder
S.key = C.key
S.mind.assigned_role = S.name
S.mind.special_role = S.name
S.mind.add_antag_datum(antag_type)
to_chat(S, S.playstyle_string)
to_chat(S, "<B>You are currently not currently in the same plane of existence as the station. \
Ctrl+Click a blood pool to manifest.</B>")
/obj/item/antag_spawner/slaughter_demon/laughter
name = "vial of tickles"
desc = "A magically infused bottle of clown love, distilled from countless hugging attacks. Used in funny rituals to attract adorable creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
color = "#FF69B4" // HOT PINK
veil_msg = "<span class='warning'>You sense an adorable presence lurking just beyond the veil...</span>"
demon_type = /mob/living/simple_animal/slaughter/laughter
antag_type = /datum/antagonist/slaughter/laughter
@@ -1,222 +0,0 @@
#define ABDUCTOR_MAX_TEAMS 4
/datum/antagonist/abductor
name = "Abductor"
roundend_category = "abductors"
antagpanel_category = "Abductor"
job_rank = ROLE_ABDUCTOR
show_in_antagpanel = FALSE //should only show subtypes
var/datum/team/abductor_team/team
var/sub_role
var/outfit
var/landmark_type
var/greet_text
/datum/antagonist/abductor/agent
name = "Abductor Agent"
sub_role = "Agent"
outfit = /datum/outfit/abductor/agent
landmark_type = /obj/effect/landmark/abductor/agent
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
show_in_antagpanel = TRUE
/datum/antagonist/abductor/scientist
name = "Abductor Scientist"
sub_role = "Scientist"
outfit = /datum/outfit/abductor/scientist
landmark_type = /obj/effect/landmark/abductor/scientist
greet_text = "Use your experimental console and surgical equipment to monitor your agent and experiment upon abducted humans."
show_in_antagpanel = TRUE
/datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team)
if(!new_team)
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
team = new_team
/datum/antagonist/abductor/get_team()
return team
/datum/antagonist/abductor/on_gain()
owner.special_role = "[name]"
owner.assigned_role = "[name]"
objectives += team.objectives
finalize_abductor()
ADD_TRAIT(owner, TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/on_removal()
if(owner.current)
to_chat(owner.current,"<span class='userdanger'>You are no longer the [owner.special_role]!</span>")
owner.special_role = null
REMOVE_TRAIT(owner, TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/greet()
to_chat(owner.current, "<span class='notice'>You are the [owner.special_role]!</span>")
to_chat(owner.current, "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>")
to_chat(owner.current, "<span class='notice'>Try not to disturb the habitat, it could lead to dead specimens.</span>")
to_chat(owner.current, "<span class='notice'>[greet_text]</span>")
owner.announce_objectives()
/datum/antagonist/abductor/proc/finalize_abductor()
//Equip
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/abductor)
H.real_name = "[team.name] [sub_role]"
H.equipOutfit(outfit)
//Teleport to ship
for(var/obj/effect/landmark/abductor/LM in GLOB.landmarks_list)
if(istype(LM, landmark_type) && LM.team_number == team.team_number)
H.forceMove(LM.loc)
break
update_abductor_icons_added(owner,"abductor")
/datum/antagonist/abductor/scientist/on_gain()
ADD_TRAIT(owner, TRAIT_ABDUCTOR_SCIENTIST_TRAINING, ABDUCTOR_ANTAGONIST)
ADD_TRAIT(owner, TRAIT_SURGEON, ABDUCTOR_ANTAGONIST)
. = ..()
/datum/antagonist/abductor/scientist/on_removal()
REMOVE_TRAIT(owner, TRAIT_ABDUCTOR_SCIENTIST_TRAINING, ABDUCTOR_ANTAGONIST)
REMOVE_TRAIT(owner, TRAIT_SURGEON, ABDUCTOR_ANTAGONIST)
. = ..()
/datum/antagonist/abductor/admin_add(datum/mind/new_owner,mob/admin)
var/list/current_teams = list()
for(var/datum/team/abductor_team/T in get_all_teams(/datum/team/abductor_team))
current_teams[T.name] = T
var/choice = input(admin,"Add to which team ?") as null|anything in (current_teams + "new team")
if (choice == "new team")
team = new
else if(choice in current_teams)
team = current_teams[choice]
else
return
new_owner.add_antag_datum(src)
log_admin("[key_name(usr)] made [key_name(new_owner)] [name] on [choice]!")
message_admins("[key_name_admin(usr)] made [key_name_admin(new_owner)] [name] on [choice] !")
/datum/antagonist/abductor/get_admin_commands()
. = ..()
.["Equip"] = CALLBACK(src,.proc/admin_equip)
/datum/antagonist/abductor/proc/admin_equip(mob/admin)
if(!ishuman(owner.current))
to_chat(admin, "<span class='warning'>This only works on humans!</span>")
return
var/mob/living/carbon/human/H = owner.current
var/gear = alert(admin,"Agent or Scientist Gear","Gear","Agent","Scientist")
if(gear)
if(gear=="Agent")
H.equipOutfit(/datum/outfit/abductor/agent)
else
H.equipOutfit(/datum/outfit/abductor/scientist)
/datum/team/abductor_team
member_name = "abductor"
var/team_number
var/list/datum/mind/abductees = list()
var/static/team_count = 1
/datum/team/abductor_team/New()
..()
team_number = team_count++
name = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
add_objective(new/datum/objective/experiment)
/datum/team/abductor_team/is_solo()
return FALSE
/datum/team/abductor_team/proc/add_objective(datum/objective/O)
O.team = src
O.update_explanation_text()
objectives += O
/datum/team/abductor_team/roundend_report()
var/list/result = list()
var/won = TRUE
for(var/datum/objective/O in objectives)
if(!O.check_completion())
won = FALSE
if(won)
result += "<span class='greentext big'>[name] team fulfilled its mission!</span>"
else
result += "<span class='redtext big'>[name] team failed its mission.</span>"
result += "<span class='header'>The abductors of [name] were:</span>"
for(var/datum/mind/abductor_mind in members)
result += printplayer(abductor_mind)
result += printobjectives(objectives)
return "<div class='panel redborder'>[result.Join("<br>")]</div>"
/datum/antagonist/abductee
name = "Abductee"
roundend_category = "abductees"
antagpanel_category = "Abductee"
/datum/antagonist/abductee/on_gain()
give_objective()
. = ..()
/datum/antagonist/abductee/greet()
to_chat(owner, "<span class='warning'><b>Your mind snaps!</b></span>")
to_chat(owner, "<big><span class='warning'><b>You can't remember how you got here...</b></span></big>")
owner.announce_objectives()
/datum/antagonist/abductee/proc/give_objective()
var/mob/living/carbon/human/H = owner.current
if(istype(H))
H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
var/datum/objective/abductee/O = new objtype()
objectives += O
/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
update_abductor_icons_added(mob_override ? mob_override.mind : owner,"abductee")
/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
// LANDMARKS
/obj/effect/landmark/abductor
var/team_number = 1
/obj/effect/landmark/abductor/agent
icon_state = "abductor_agent"
/obj/effect/landmark/abductor/scientist
icon_state = "abductor"
// OBJECTIVES
/datum/objective/experiment
target_amount = 6
/datum/objective/experiment/New()
explanation_text = "Experiment on [target_amount] humans."
/datum/objective/experiment/check_completion()
for(var/obj/machinery/abductor/experiment/E in GLOB.machines)
if(!istype(team, /datum/team/abductor_team))
return FALSE
var/datum/team/abductor_team/T = team
if(E.team_number == T.team_number)
return E.points >= target_amount
return FALSE
/datum/antagonist/proc/update_abductor_icons_added(datum/mind/alien_mind,hud_type)
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR]
hud.join_hud(alien_mind.current)
set_antag_hud(alien_mind.current, hud_type)
/datum/antagonist/proc/update_abductor_icons_removed(datum/mind/alien_mind)
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR]
hud.leave_hud(alien_mind.current)
set_antag_hud(alien_mind.current, null)
@@ -1,800 +0,0 @@
#define VEST_STEALTH 1
#define VEST_COMBAT 2
#define GIZMO_SCAN 1
#define GIZMO_MARK 2
#define MIND_DEVICE_MESSAGE 1
#define MIND_DEVICE_CONTROL 2
//AGENT VEST
/obj/item/clothing/suit/armor/abductor/vest
name = "agent vest"
desc = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
icon = 'icons/obj/abductor.dmi'
icon_state = "vest_stealth"
item_state = "armor"
blood_overlay_type = "armor"
armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 15, "bio" = 15, "rad" = 15, "fire" = 70, "acid" = 70)
actions_types = list(/datum/action/item_action/hands_free/activate)
allowed = list(
/obj/item/abductor,
/obj/item/abductor_baton,
/obj/item/melee/baton,
/obj/item/gun/energy,
/obj/item/restraints/handcuffs
)
var/mode = VEST_STEALTH
var/stealth_active = 0
var/combat_cooldown = 10
var/datum/icon_snapshot/disguise
var/stealth_armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 15, "bio" = 15, "rad" = 15, "fire" = 70, "acid" = 70)
var/combat_armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 90, "acid" = 90)
/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop()
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT))
REMOVE_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
else
ADD_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
if(ismob(loc))
to_chat(loc, "<span class='notice'>Your vest is now [HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "locked" : "unlocked"].</span>")
/obj/item/clothing/suit/armor/abductor/vest/proc/flip_mode()
switch(mode)
if(VEST_STEALTH)
mode = VEST_COMBAT
DeactivateStealth()
armor = combat_armor
icon_state = "vest_combat"
if(VEST_COMBAT)// TO STEALTH
mode = VEST_STEALTH
armor = stealth_armor
icon_state = "vest_stealth"
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_wear_suit()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_WEAR_SUIT) //we only give the mob the ability to activate the vest if he's actually wearing it.
return 1
/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry)
disguise = entry
/obj/item/clothing/suit/armor/abductor/vest/proc/ActivateStealth()
if(disguise == null)
return
stealth_active = 1
if(ishuman(loc))
var/mob/living/carbon/human/M = loc
new /obj/effect/temp_visual/dir_setting/ninja/cloak(get_turf(M), M.dir)
M.name_override = disguise.name
M.icon = disguise.icon
M.icon_state = disguise.icon_state
M.cut_overlays()
M.add_overlay(disguise.overlays)
M.update_inv_hands()
/obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth()
if(!stealth_active)
return
stealth_active = 0
if(ishuman(loc))
var/mob/living/carbon/human/M = loc
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(M), M.dir)
M.name_override = null
M.cut_overlays()
M.regenerate_icons()
/obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
DeactivateStealth()
return 0
/obj/item/clothing/suit/armor/abductor/vest/IsReflect()
DeactivateStealth()
return 0
/obj/item/clothing/suit/armor/abductor/vest/ui_action_click()
switch(mode)
if(VEST_COMBAT)
Adrenaline()
if(VEST_STEALTH)
if(stealth_active)
DeactivateStealth()
else
ActivateStealth()
/obj/item/clothing/suit/armor/abductor/vest/proc/Adrenaline()
if(ishuman(loc))
if(combat_cooldown != initial(combat_cooldown))
to_chat(loc, "<span class='warning'>Combat injection is still recharging.</span>")
return
var/mob/living/carbon/human/M = loc
M.do_adrenaline(150, FALSE, 0, 0, TRUE, list("inaprovaline" = 3, "synaptizine" = 10, "omnizine" = 10), "<span class='boldnotice'>You feel a sudden surge of energy!</span>")
combat_cooldown = 0
START_PROCESSING(SSobj, src)
/obj/item/clothing/suit/armor/abductor/vest/process()
combat_cooldown++
if(combat_cooldown==initial(combat_cooldown))
STOP_PROCESSING(SSobj, src)
/obj/item/clothing/suit/armor/abductor/Destroy()
STOP_PROCESSING(SSobj, src)
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.vest == src)
C.vest = null
break
. = ..()
/obj/item/abductor
icon = 'icons/obj/abductor.dmi'
/obj/item/abductor/proc/AbductorCheck(mob/user)
if(HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING))
return TRUE
if (istype(user) && user.mind && HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
return TRUE
to_chat(user, "<span class='warning'>You can't figure how this works!</span>")
return FALSE
/obj/item/abductor/proc/ScientistCheck(mob/user)
var/training = HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) || (user.mind && HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
var/sci_training = HAS_TRAIT(user, TRAIT_ABDUCTOR_SCIENTIST_TRAINING) || (user.mind && HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_SCIENTIST_TRAINING))
if(training && !sci_training)
to_chat(user, "<span class='warning'>You're not trained to use this!</span>")
. = FALSE
else if(!training && !sci_training)
to_chat(user, "<span class='warning'>You can't figure how this works!</span>")
. = FALSE
else
. = TRUE
/obj/item/abductor/gizmo
name = "science tool"
desc = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
icon_state = "gizmo_scan"
item_state = "silencer"
lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
var/mode = GIZMO_SCAN
var/mob/living/marked = null
var/obj/machinery/abductor/console/console
/obj/item/abductor/gizmo/attack_self(mob/user)
if(!ScientistCheck(user))
return
if(!console)
to_chat(user, "<span class='warning'>The device is not linked to console!</span>")
return
if(mode == GIZMO_SCAN)
mode = GIZMO_MARK
icon_state = "gizmo_mark"
else
mode = GIZMO_SCAN
icon_state = "gizmo_scan"
to_chat(user, "<span class='notice'>You switch the device to [mode==GIZMO_SCAN? "SCAN": "MARK"] MODE</span>")
/obj/item/abductor/gizmo/attack(mob/living/M, mob/user)
if(!ScientistCheck(user))
return
if(!console)
to_chat(user, "<span class='warning'>The device is not linked to console!</span>")
return
switch(mode)
if(GIZMO_SCAN)
scan(M, user)
if(GIZMO_MARK)
mark(M, user)
/obj/item/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(flag)
return
if(!ScientistCheck(user))
return
if(!console)
to_chat(user, "<span class='warning'>The device is not linked to console!</span>")
return
switch(mode)
if(GIZMO_SCAN)
scan(target, user)
if(GIZMO_MARK)
mark(target, user)
/obj/item/abductor/gizmo/proc/scan(atom/target, mob/living/user)
if(ishuman(target))
console.AddSnapshot(target)
to_chat(user, "<span class='notice'>You scan [target] and add [target.p_them()] to the database.</span>")
/obj/item/abductor/gizmo/proc/mark(atom/target, mob/living/user)
if(marked == target)
to_chat(user, "<span class='warning'>This specimen is already marked!</span>")
return
if(ishuman(target))
if(isabductor(target))
marked = target
to_chat(user, "<span class='notice'>You mark [target] for future retrieval.</span>")
else
prepare(target,user)
else
prepare(target,user)
/obj/item/abductor/gizmo/proc/prepare(atom/target, mob/living/user)
if(get_dist(target,user)>1)
to_chat(user, "<span class='warning'>You need to be next to the specimen to prepare it for transport!</span>")
return
to_chat(user, "<span class='notice'>You begin preparing [target] for transport...</span>")
if(do_after(user, 100, target = target))
marked = target
to_chat(user, "<span class='notice'>You finish preparing [target] for transport.</span>")
/obj/item/abductor/gizmo/Destroy()
if(console)
console.gizmo = null
. = ..()
/obj/item/abductor/silencer
name = "abductor silencer"
desc = "A compact device used to shut down communications equipment."
icon_state = "silencer"
item_state = "gizmo"
lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
/obj/item/abductor/silencer/attack(mob/living/M, mob/user)
if(!AbductorCheck(user))
return
radio_off(M, user)
/obj/item/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(flag)
return
if(!AbductorCheck(user))
return
radio_off(target, user)
/obj/item/abductor/silencer/proc/radio_off(atom/target, mob/living/user)
if( !(user in (viewers(7,target))) )
return
var/turf/targloc = get_turf(target)
var/mob/living/carbon/human/M
for(M in view(2,targloc))
if(M == user)
continue
to_chat(user, "<span class='notice'>You silence [M]'s radio devices.</span>")
radio_off_mob(M)
/obj/item/abductor/silencer/proc/radio_off_mob(mob/living/carbon/human/M)
var/list/all_items = M.GetAllContents()
for(var/obj/I in all_items)
if(istype(I, /obj/item/radio/))
var/obj/item/radio/r = I
r.listening = 0
if(!istype(I, /obj/item/radio/headset))
r.broadcasting = 0 //goddamned headset hacks
/obj/item/abductor/mind_device
name = "mental interface device"
desc = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \
or to send a command to a test subject with a charged gland."
icon_state = "mind_device_message"
item_state = "silencer"
lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
var/mode = MIND_DEVICE_MESSAGE
/obj/item/abductor/mind_device/attack_self(mob/user)
if(!ScientistCheck(user))
return
if(mode == MIND_DEVICE_MESSAGE)
mode = MIND_DEVICE_CONTROL
icon_state = "mind_device_control"
else
mode = MIND_DEVICE_MESSAGE
icon_state = "mind_device_message"
to_chat(user, "<span class='notice'>You switch the device to [mode==MIND_DEVICE_MESSAGE? "TRANSMISSION": "COMMAND"] MODE</span>")
/obj/item/abductor/mind_device/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
if(!ScientistCheck(user))
return
switch(mode)
if(MIND_DEVICE_CONTROL)
mind_control(target, user)
if(MIND_DEVICE_MESSAGE)
mind_message(target, user)
/obj/item/abductor/mind_device/proc/mind_control(atom/target, mob/living/user)
if(iscarbon(target))
var/mob/living/carbon/C = target
var/obj/item/organ/heart/gland/G = C.getorganslot("heart")
if(!istype(G))
to_chat(user, "<span class='warning'>Your target does not have an experimental gland!</span>")
return
if(!G.mind_control_uses)
to_chat(user, "<span class='warning'>Your target's gland is spent!</span>")
return
if(G.active_mind_control)
to_chat(user, "<span class='warning'>Your target is already under a mind-controlling influence!</span>")
return
var/command = stripped_input(user, "Enter the command for your target to follow.\
Uses Left: [G.mind_control_uses], Duration: [DisplayTimeText(G.mind_control_duration)]","Enter command")
if(!command)
return
if(QDELETED(user) || user.get_active_held_item() != src || loc != user)
return
if(QDELETED(G))
return
if(C.anti_magic_check(FALSE, FALSE, TRUE, 0))
to_chat(user, "<span class='warning'>Your target seems to have some sort of tinfoil protection on, blocking the message from being sent!</span>")
return
G.mind_control(command, user)
to_chat(user, "<span class='notice'>You send the command to your target.</span>")
/obj/item/abductor/mind_device/proc/mind_message(atom/target, mob/living/user)
if(isliving(target))
var/mob/living/L = target
if(L.stat == DEAD)
to_chat(user, "<span class='warning'>Your target is dead!</span>")
return
var/message = stripped_input(user, "Write a message to send to your target's brain.","Enter message")
if(!message)
return
if(QDELETED(L) || L.stat == DEAD)
return
to_chat(L, "<span class='italics'>You hear a voice in your head saying: </span><span class='abductor'>[message]</span>")
to_chat(user, "<span class='notice'>You send the message to your target.</span>")
log_directed_talk(user, L, message, LOG_SAY, "abductor whisper")
/obj/item/firing_pin/abductor
name = "alien firing pin"
icon_state = "firing_pin_ayy"
desc = "This firing pin is slimy and warm; you can swear you feel it \
constantly trying to mentally probe you."
fail_message = "<span class='abductor'>\
Firing error, please contact Command.</span>"
/obj/item/firing_pin/abductor/pin_auth(mob/living/user)
. = isabductor(user)
/obj/item/gun/energy/alien
name = "alien pistol"
desc = "A complicated gun that fires bursts of high-intensity radiation."
ammo_type = list(/obj/item/ammo_casing/energy/declone)
pin = /obj/item/firing_pin/abductor
icon_state = "alienpistol"
item_state = "alienpistol"
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
/obj/item/paper/guides/antag/abductor
name = "Dissection Guide"
icon_state = "alienpaper_words"
info = {"<b>Dissection for Dummies</b><br>
<br>
1.Acquire fresh specimen.<br>
2.Put the specimen on operating table.<br>
3.Apply surgical drapes, preparing for experimental dissection.<br>
4.Apply scalpel to specimen's torso.<br>
5.Clamp bleeders on specimen's torso with a hemostat.<br>
6.Retract skin of specimen's torso with a retractor.<br>
7.Apply scalpel again to specimen's torso.<br>
8.Search through the specimen's torso with your hands to remove any superfluous organs.<br>
9.Insert replacement gland (Retrieve one from gland storage).<br>
10.Consider dressing the specimen back to not disturb the habitat. <br>
11.Put the specimen in the experiment machinery.<br>
12.Choose one of the machine options. The target will be analyzed and teleported to the selected drop-off point.<br>
13.You will receive one supply credit, and the subject will be counted towards your quota.<br>
<br>
Congratulations! You are now trained for invasive xenobiology research!"}
/obj/item/paper/guides/antag/abductor/update_icon()
return
/obj/item/paper/guides/antag/abductor/AltClick()
return //otherwise it would fold into a paperplane.
#define BATON_STUN 0
#define BATON_SLEEP 1
#define BATON_CUFF 2
#define BATON_PROBE 3
#define BATON_MODES 4
/obj/item/abductor_baton
name = "advanced baton"
desc = "A quad-mode baton used for incapacitation and restraining of specimens."
var/mode = BATON_STUN
icon = 'icons/obj/abductor.dmi'
icon_state = "wonderprodStun"
item_state = "wonderprod"
lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
force = 7
w_class = WEIGHT_CLASS_NORMAL
actions_types = list(/datum/action/item_action/toggle_mode)
/obj/item/abductor_baton/proc/toggle(mob/living/user=usr)
mode = (mode+1)%BATON_MODES
var/txt
switch(mode)
if(BATON_STUN)
txt = "stunning"
if(BATON_SLEEP)
txt = "sleep inducement"
if(BATON_CUFF)
txt = "restraining"
if(BATON_PROBE)
txt = "probing"
to_chat(usr, "<span class='notice'>You switch the baton to [txt] mode.</span>")
update_icon()
/obj/item/abductor_baton/update_icon()
switch(mode)
if(BATON_STUN)
icon_state = "wonderprodStun"
item_state = "wonderprodStun"
if(BATON_SLEEP)
icon_state = "wonderprodSleep"
item_state = "wonderprodSleep"
if(BATON_CUFF)
icon_state = "wonderprodCuff"
item_state = "wonderprodCuff"
if(BATON_PROBE)
icon_state = "wonderprodProbe"
item_state = "wonderprodProbe"
/obj/item/abductor_baton/attack(mob/target, mob/living/user)
if(!isabductor(user))
return
if(iscyborg(target))
..()
return
if(!isliving(target))
return
var/mob/living/L = target
user.do_attack_animation(L)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return 0
switch (mode)
if(BATON_STUN)
StunAttack(L,user)
if(BATON_SLEEP)
SleepAttack(L,user)
if(BATON_CUFF)
CuffAttack(L,user)
if(BATON_PROBE)
ProbeAttack(L,user)
/obj/item/abductor_baton/attack_self(mob/living/user)
toggle(user)
/obj/item/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user)
L.lastattacker = user.real_name
L.lastattackerckey = user.ckey
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
L.Knockdown(140)
L.apply_effect(EFFECT_STUTTER, 7)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
L.visible_message("<span class='danger'>[user] has stunned [L] with [src]!</span>", \
"<span class='userdanger'>[user] has stunned you with [src]!</span>")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.forcesay(GLOB.hit_appends)
log_combat(user, L, "stunned")
/obj/item/abductor_baton/proc/SleepAttack(mob/living/L,mob/living/user)
if(L.incapacitated(TRUE, TRUE))
if(L.anti_magic_check(FALSE, FALSE, TRUE, 0))
to_chat(user, "<span class='warning'>The specimen's tinfoil protection is interfering with the sleep inducement!</span>")
L.visible_message("<span class='danger'>[user] tried to induced sleep in [L] with [src], but [L.p_their()] tinfoil protected [L.p_them()]!</span>", \
"<span class='userdanger'>You feel a strange wave of heavy drowsiness wash over you, but your tinfoil protection deflects most of it!</span>")
L.drowsyness += 2
return
L.visible_message("<span class='danger'>[user] has induced sleep in [L] with [src]!</span>", \
"<span class='userdanger'>You suddenly feel very drowsy!</span>")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
L.Sleeping(1200)
log_combat(user, L, "put to sleep")
else
if(L.anti_magic_check(FALSE, FALSE, TRUE, 0))
to_chat(user, "<span class='warning'>The specimen's tinfoil protection is completely blocking our sleep inducement methods!</span>")
L.visible_message("<span class='danger'>[user] tried to induce sleep in [L] with [src], but [L.p_their()] tinfoil completely protected [L.p_them()]!</span>", \
"<span class='userdanger'>Any sense of drowsiness is quickly diminished as your tinfoil protection deflects the effects!</span>")
return
L.drowsyness += 1
to_chat(user, "<span class='warning'>Sleep inducement works fully only on stunned specimens! </span>")
L.visible_message("<span class='danger'>[user] tried to induce sleep in [L] with [src]!</span>", \
"<span class='userdanger'>You suddenly feel drowsy!</span>")
/obj/item/abductor_baton/proc/CuffAttack(mob/living/L,mob/living/user)
if(!iscarbon(L))
return
var/mob/living/carbon/C = L
if(!C.handcuffed)
if(C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore())
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
C.visible_message("<span class='danger'>[user] begins restraining [C] with [src]!</span>", \
"<span class='userdanger'>[user] begins shaping an energy field around your hands!</span>")
if(do_mob(user, C, 30) && (C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore()))
if(!C.handcuffed)
C.handcuffed = new /obj/item/restraints/handcuffs/energy/used(C)
C.update_handcuffed()
to_chat(user, "<span class='notice'>You restrain [C].</span>")
log_combat(user, C, "handcuffed")
else
to_chat(user, "<span class='warning'>You fail to restrain [C].</span>")
else
to_chat(user, "<span class='warning'>[C] doesn't have two hands...</span>")
/obj/item/abductor_baton/proc/ProbeAttack(mob/living/L,mob/living/user)
L.visible_message("<span class='danger'>[user] probes [L] with [src]!</span>", \
"<span class='userdanger'>[user] probes you!</span>")
var/species = "<span class='warning'>Unknown species</span>"
var/helptext = "<span class='warning'>Species unsuitable for experiments.</span>"
if(ishuman(L))
var/mob/living/carbon/human/H = L
species = "<span class='notice'>[H.dna.species.name]</span>"
if(L.mind && L.mind.has_antag_datum(/datum/antagonist/changeling))
species = "<span class='warning'>Changeling lifeform</span>"
var/obj/item/organ/heart/gland/temp = locate() in H.internal_organs
if(temp)
helptext = "<span class='warning'>Experimental gland detected!</span>"
else
if (L.getorganslot(ORGAN_SLOT_HEART))
helptext = "<span class='notice'>Subject suitable for experiments.</span>"
else
helptext = "<span class='warning'>Subject unsuitable for experiments.</span>"
to_chat(user, "<span class='notice'>Probing result:</span>[species]")
to_chat(user, "[helptext]")
/obj/item/restraints/handcuffs/energy
name = "hard-light energy field"
desc = "A hard-light field restraining the hands."
icon_state = "cuff" // Needs sprite
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
breakouttime = 450
trashtype = /obj/item/restraints/handcuffs/energy/used
flags_1 = NONE
/obj/item/restraints/handcuffs/energy/used
item_flags = DROPDEL
/obj/item/restraints/handcuffs/energy/used/dropped(mob/user)
user.visible_message("<span class='danger'>[user]'s [name] breaks in a discharge of energy!</span>", \
"<span class='userdanger'>[user]'s [name] breaks in a discharge of energy!</span>")
var/datum/effect_system/spark_spread/S = new
S.set_up(4,0,user.loc)
S.start()
. = ..()
/obj/item/abductor_baton/examine(mob/user)
..()
switch(mode)
if(BATON_STUN)
to_chat(user, "<span class='warning'>The baton is in stun mode.</span>")
if(BATON_SLEEP)
to_chat(user, "<span class='warning'>The baton is in sleep inducement mode.</span>")
if(BATON_CUFF)
to_chat(user, "<span class='warning'>The baton is in restraining mode.</span>")
if(BATON_PROBE)
to_chat(user, "<span class='warning'>The baton is in probing mode.</span>")
/obj/item/radio/headset/abductor
name = "alien headset"
desc = "An advanced alien headset designed to monitor communications of human space stations. Why does it have a microphone? No one knows."
icon = 'icons/obj/abductor.dmi'
icon_state = "abductor_headset"
item_state = "abductor_headset"
keyslot2 = new /obj/item/encryptionkey/heads/captain
/obj/item/radio/headset/abductor/Initialize(mapload)
. = ..()
make_syndie()
/obj/item/radio/headset/abductor/ComponentInitialize()
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
/obj/item/radio/headset/abductor/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/screwdriver))
return // Stops humans from disassembling abductor headsets.
return ..()
/obj/item/scalpel/alien
name = "alien scalpel"
desc = "It's a gleaming sharp knife made out of silvery-green metal."
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/hemostat/alien
name = "alien hemostat"
desc = "You've never seen this before."
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/retractor/alien
name = "alien retractor"
desc = "You're not sure if you want the veil pulled back."
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/circular_saw/alien
name = "alien saw"
desc = "Do the aliens also lose this, and need to find an alien hatchet?"
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/surgicaldrill/alien
name = "alien drill"
desc = "Maybe alien surgeons have finally found a use for the drill."
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/cautery/alien
name = "alien cautery"
desc = "Why would bloodless aliens have a tool to stop bleeding? \
Unless..."
icon = 'icons/obj/abductor.dmi'
toolspeed = 0.25
/obj/item/clothing/head/helmet/abductor
name = "agent headgear"
desc = "Abduct with style - spiky style. Prevents digital tracking."
icon_state = "alienhelmet"
item_state = "alienhelmet"
blockTracking = TRUE
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
// Operating Table / Beds / Lockers
/obj/structure/bed/abductor
name = "resting contraption"
desc = "This looks similar to contraptions from Earth. Could aliens be stealing our technology?"
icon = 'icons/obj/abductor.dmi'
buildstacktype = /obj/item/stack/sheet/mineral/abductor
icon_state = "bed"
/obj/structure/table_frame/abductor
name = "alien table frame"
desc = "A sturdy table frame made from alien alloy."
icon_state = "alien_frame"
framestack = /obj/item/stack/sheet/mineral/abductor
framestackamount = 1
/obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wrench))
to_chat(user, "<span class='notice'>You start disassembling [src]...</span>")
I.play_tool_sound(src)
if(I.use_tool(src, user, 30))
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
for(var/i = 1, i <= framestackamount, i++)
new framestack(get_turf(src))
qdel(src)
return
if(istype(I, /obj/item/stack/sheet/mineral/abductor))
var/obj/item/stack/sheet/P = I
if(P.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one alien alloy sheet to do this!</span>")
return
to_chat(user, "<span class='notice'>You start adding [P] to [src]...</span>")
if(do_after(user, 50, target = src))
P.use(1)
new /obj/structure/table/abductor(src.loc)
qdel(src)
return
if(istype(I, /obj/item/stack/sheet/mineral/silver))
var/obj/item/stack/sheet/P = I
if(P.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one sheet of silver to do this!</span>")
return
to_chat(user, "<span class='notice'>You start adding [P] to [src]...</span>")
if(do_after(user, 50, target = src))
P.use(1)
new /obj/structure/table/optable/abductor(src.loc)
qdel(src)
/obj/structure/table/abductor
name = "alien table"
desc = "Advanced flat surface technology at work!"
icon = 'icons/obj/smooth_structures/alien_table.dmi'
icon_state = "alien_table"
buildstack = /obj/item/stack/sheet/mineral/abductor
framestack = /obj/item/stack/sheet/mineral/abductor
buildstackamount = 1
framestackamount = 1
canSmoothWith = null
frame = /obj/structure/table_frame/abductor
/obj/structure/table/optable/abductor
name = "alien operating table"
desc = "Used for alien medical procedures. The surface is covered in tiny spines."
frame = /obj/structure/table_frame/abductor
buildstack = /obj/item/stack/sheet/mineral/silver
framestack = /obj/item/stack/sheet/mineral/abductor
buildstackamount = 1
framestackamount = 1
icon = 'icons/obj/abductor.dmi'
icon_state = "bed"
can_buckle = 1
buckle_lying = 1
var/static/list/injected_reagents = list("corazone")
/obj/structure/table/optable/abductor/Crossed(atom/movable/AM)
. = ..()
if(iscarbon(AM))
START_PROCESSING(SSobj, src)
to_chat(AM, "<span class='danger'>You feel a series of tiny pricks!</span>")
/obj/structure/table/optable/abductor/process()
. = PROCESS_KILL
for(var/mob/living/carbon/C in get_turf(src))
. = TRUE
for(var/chemical in injected_reagents)
if(C.reagents.get_reagent_amount(chemical) < 1)
C.reagents.add_reagent(chemical, 1)
/obj/structure/table/optable/abductor/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/structure/closet/abductor
name = "alien locker"
desc = "Contains secrets of the universe."
icon_state = "abductor"
icon_door = "abductor"
can_weld_shut = FALSE
material_drop = /obj/item/stack/sheet/mineral/abductor
/obj/structure/door_assembly/door_assembly_abductor
name = "alien airlock assembly"
icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
base_name = "alien airlock"
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
airlock_type = /obj/machinery/door/airlock/abductor
material_type = /obj/item/stack/sheet/mineral/abductor
noglass = TRUE
@@ -24,7 +24,7 @@
for(var/obj/item/abductor/gizmo/G in B.contents)
console.AddGizmo(G)
/datum/outfit/abductor/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
/datum/outfit/abductor/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
..()
if(!visualsOnly)
link_to_console(H)
@@ -49,7 +49,7 @@
/obj/item/abductor/gizmo = 1
)
/datum/outfit/abductor/scientist/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
/datum/outfit/abductor/scientist/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
..()
if(!visualsOnly)
var/obj/item/implant/abductor/beamplant = new
@@ -1,354 +0,0 @@
/obj/item/organ/heart/gland
name = "fleshy mass"
desc = "A nausea-inducing hunk of twisting flesh and metal."
icon = 'icons/obj/abductor.dmi'
icon_state = "gland"
status = ORGAN_ROBOTIC
beating = TRUE
var/true_name = "baseline placebo referencer"
var/cooldown_low = 300
var/cooldown_high = 300
var/next_activation = 0
var/uses // -1 For infinite
var/human_only = 0
var/active = 0
var/mind_control_uses = 1
var/mind_control_duration = 1800
var/active_mind_control = FALSE
/obj/item/organ/heart/gland/examine(mob/user)
. = ..()
if((user.mind && HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_SCIENTIST_TRAINING)) || isobserver(user))
to_chat(user, "<span class='notice'>It is \a [true_name].</span>")
/obj/item/organ/heart/gland/proc/ownerCheck()
if(ishuman(owner))
return TRUE
if(!human_only && iscarbon(owner))
return TRUE
return FALSE
/obj/item/organ/heart/gland/proc/Start()
active = 1
next_activation = world.time + rand(cooldown_low,cooldown_high)
/obj/item/organ/heart/gland/proc/update_gland_hud()
if(!owner)
return
var/image/holder = owner.hud_list[GLAND_HUD]
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
holder.pixel_y = I.Height() - world.icon_size
if(active_mind_control)
holder.icon_state = "hudgland_active"
else if(mind_control_uses)
holder.icon_state = "hudgland_ready"
else
holder.icon_state = "hudgland_spent"
/obj/item/organ/heart/gland/proc/mind_control(command, mob/living/user)
if(!ownerCheck() || !mind_control_uses || active_mind_control)
return FALSE
mind_control_uses--
to_chat(owner, "<span class='userdanger'>You suddenly feel an irresistible compulsion to follow an order...</span>")
to_chat(owner, "<span class='mind_control'>[command]</span>")
active_mind_control = TRUE
message_admins("[key_name(user)] sent an abductor mind control message to [key_name(owner)]: [command]")
update_gland_hud()
addtimer(CALLBACK(src, .proc/clear_mind_control), mind_control_duration)
/obj/item/organ/heart/gland/proc/clear_mind_control()
if(!ownerCheck() || !active_mind_control)
return FALSE
to_chat(owner, "<span class='userdanger'>You feel the compulsion fade, and you completely forget about your previous orders.</span>")
active_mind_control = FALSE
/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0)
active = 0
if(initial(uses) == 1)
uses = initial(uses)
var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
hud.remove_from_hud(owner)
clear_mind_control()
..()
/obj/item/organ/heart/gland/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
..()
if(special != 2 && uses) // Special 2 means abductor surgery
Start()
var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
hud.add_to_hud(owner)
update_gland_hud()
/obj/item/organ/heart/gland/on_life()
if(!beating)
// alien glands are immune to stopping.
beating = TRUE
if(!active)
return
if(!ownerCheck())
active = 0
return
if(next_activation <= world.time)
activate()
uses--
next_activation = world.time + rand(cooldown_low,cooldown_high)
if(!uses)
active = 0
/obj/item/organ/heart/gland/proc/activate()
return
/obj/item/organ/heart/gland/heals
true_name = "coherency harmonizer"
cooldown_low = 200
cooldown_high = 400
uses = -1
icon_state = "health"
mind_control_uses = 3
mind_control_duration = 3000
/obj/item/organ/heart/gland/heals/activate()
to_chat(owner, "<span class='notice'>You feel curiously revitalized.</span>")
owner.adjustToxLoss(-20, FALSE, TRUE)
owner.heal_bodypart_damage(20, 20, 0, TRUE)
owner.adjustOxyLoss(-20)
/obj/item/organ/heart/gland/slime
true_name = "gastric animation galvanizer"
cooldown_low = 600
cooldown_high = 1200
uses = -1
icon_state = "slime"
mind_control_uses = 1
mind_control_duration = 2400
/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
..()
owner.faction |= "slime"
owner.grant_language(/datum/language/slime)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "<span class='warning'>You feel nauseated!</span>")
owner.vomit(20)
var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), "grey")
Slime.Friends = list(owner)
Slime.Leader = owner
/obj/item/organ/heart/gland/mindshock
true_name = "neural crosstalk uninhibitor"
cooldown_low = 400
cooldown_high = 700
uses = -1
icon_state = "mindshock"
mind_control_uses = 1
mind_control_duration = 6000
/obj/item/organ/heart/gland/mindshock/activate()
to_chat(owner, "<span class='notice'>You get a headache.</span>")
var/turf/T = get_turf(owner)
for(var/mob/living/carbon/H in orange(4,T))
if(H == owner)
continue
switch(pick(1,3))
if(1)
to_chat(H, "<span class='userdanger'>You hear a loud buzz in your head, silencing your thoughts!</span>")
H.Stun(50)
if(2)
to_chat(H, "<span class='warning'>You hear an annoying buzz in your head.</span>")
H.confused += 15
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 160)
if(3)
H.hallucination += 60
/obj/item/organ/heart/gland/pop
true_name = "anthropmorphic translocator"
cooldown_low = 900
cooldown_high = 1800
uses = -1
human_only = TRUE
icon_state = "species"
mind_control_uses = 5
mind_control_duration = 300
/obj/item/organ/heart/gland/pop/activate()
to_chat(owner, "<span class='notice'>You feel unlike yourself.</span>")
randomize_human(owner)
var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/insect, /datum/species/fly))
owner.set_species(species)
/obj/item/organ/heart/gland/ventcrawling
true_name = "pliant cartilage enabler"
cooldown_low = 1800
cooldown_high = 2400
uses = 1
icon_state = "vent"
mind_control_uses = 4
mind_control_duration = 1800
/obj/item/organ/heart/gland/ventcrawling/activate()
to_chat(owner, "<span class='notice'>You feel very stretchy.</span>")
owner.ventcrawler = VENTCRAWLER_ALWAYS
/obj/item/organ/heart/gland/viral
true_name = "contamination incubator"
cooldown_low = 1800
cooldown_high = 2400
uses = 1
icon_state = "viral"
mind_control_uses = 1
mind_control_duration = 1800
/obj/item/organ/heart/gland/viral/activate()
to_chat(owner, "<span class='warning'>You feel sick.</span>")
var/datum/disease/advance/A = random_virus(pick(2,6),6)
A.carrier = TRUE
owner.ForceContractDisease(A, FALSE, TRUE)
/obj/item/organ/heart/gland/viral/proc/random_virus(max_symptoms, max_level)
if(max_symptoms > VIRUS_SYMPTOM_LIMIT)
max_symptoms = VIRUS_SYMPTOM_LIMIT
var/datum/disease/advance/A = new /datum/disease/advance()
var/list/datum/symptom/possible_symptoms = list()
for(var/symptom in subtypesof(/datum/symptom))
var/datum/symptom/S = symptom
if(initial(S.level) > max_level)
continue
if(initial(S.level) <= 0) //unobtainable symptoms
continue
possible_symptoms += S
for(var/i in 1 to max_symptoms)
var/datum/symptom/chosen_symptom = pick_n_take(possible_symptoms)
if(chosen_symptom)
var/datum/symptom/S = new chosen_symptom
A.symptoms += S
A.Refresh() //just in case someone already made and named the same disease
return A
/obj/item/organ/heart/gland/trauma
true_name = "white matter randomiser"
cooldown_low = 800
cooldown_high = 1200
uses = 5
icon_state = "emp"
mind_control_uses = 3
mind_control_duration = 1800
/obj/item/organ/heart/gland/trauma/activate()
to_chat(owner, "<span class='warning'>You feel a spike of pain in your head.</span>")
if(prob(33))
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
else
if(prob(20))
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
else
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
/obj/item/organ/heart/gland/spiderman
true_name = "araneae cloister accelerator"
cooldown_low = 450
cooldown_high = 900
uses = -1
icon_state = "spider"
mind_control_uses = 2
mind_control_duration = 2400
/obj/item/organ/heart/gland/spiderman/activate()
to_chat(owner, "<span class='warning'>You feel something crawling in your skin.</span>")
owner.faction |= "spiders"
var/obj/structure/spider/spiderling/S = new(owner.drop_location())
S.directive = "Protect your nest inside [owner.real_name]."
/obj/item/organ/heart/gland/egg
true_name = "roe/enzymatic synthesizer"
cooldown_low = 300
cooldown_high = 400
uses = -1
icon_state = "egg"
lefthand_file = 'icons/mob/inhands/misc/food_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/food_righthand.dmi'
mind_control_uses = 2
mind_control_duration = 1800
/obj/item/organ/heart/gland/egg/activate()
owner.visible_message("<span class='alertalien'>[owner] [pick(EGG_LAYING_MESSAGES)]</span>")
var/turf/T = owner.drop_location()
new /obj/item/reagent_containers/food/snacks/egg/gland(T)
/obj/item/organ/heart/gland/electric
true_name = "electron accumulator/discharger"
cooldown_low = 800
cooldown_high = 1200
uses = -1
mind_control_uses = 2
mind_control_duration = 900
/obj/item/organ/heart/gland/electric/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
..()
ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, ORGAN_TRAIT)
/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0)
REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, ORGAN_TRAIT)
..()
/obj/item/organ/heart/gland/electric/activate()
owner.visible_message("<span class='danger'>[owner]'s skin starts emitting electric arcs!</span>",\
"<span class='warning'>You feel electric energy building up inside you!</span>")
playsound(get_turf(owner), "sparks", 100, 1, -1)
addtimer(CALLBACK(src, .proc/zap), rand(30, 100))
/obj/item/organ/heart/gland/electric/proc/zap()
tesla_zap(owner, 4, 8000, TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, 1)
/obj/item/organ/heart/gland/chem
true_name = "intrinsic pharma-provider"
cooldown_low = 50
cooldown_high = 50
uses = -1
mind_control_uses = 3
mind_control_duration = 1200
var/list/possible_reagents = list()
/obj/item/organ/heart/gland/chem/Initialize()
..()
for(var/X in subtypesof(/datum/reagent/drug))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
for(var/X in subtypesof(/datum/reagent/medicine))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
for(var/X in typesof(/datum/reagent/toxin))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
/obj/item/organ/heart/gland/chem/activate()
var/chem_to_add = pick(possible_reagents)
owner.reagents.add_reagent(chem_to_add, 2)
owner.adjustToxLoss(-2, TRUE, TRUE)
..()
/obj/item/organ/heart/gland/plasma
true_name = "effluvium sanguine-synonym emitter"
cooldown_low = 1200
cooldown_high = 1800
uses = -1
mind_control_uses = 1
mind_control_duration = 800
/obj/item/organ/heart/gland/plasma/activate()
to_chat(owner, "<span class='warning'>You feel bloated.</span>")
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, owner, "<span class='userdanger'>A massive stomachache overcomes you.</span>"), 150)
addtimer(CALLBACK(src, .proc/vomit_plasma), 200)
/obj/item/organ/heart/gland/plasma/proc/vomit_plasma()
if(!owner)
return
owner.visible_message("<span class='danger'>[owner] vomits a cloud of plasma!</span>")
var/turf/open/T = get_turf(owner)
if(istype(T))
T.atmos_spawn_air("plasma=50;TEMP=[T20C]")
owner.vomit()
@@ -1,138 +0,0 @@
/obj/machinery/computer/camera_advanced/abductor
name = "Human Observation Console"
var/team_number = 0
networks = list("ss13", "abductor")
var/datum/action/innate/teleport_in/tele_in_action = new
var/datum/action/innate/teleport_out/tele_out_action = new
var/datum/action/innate/teleport_self/tele_self_action = new
var/datum/action/innate/vest_mode_swap/vest_mode_action = new
var/datum/action/innate/vest_disguise_swap/vest_disguise_action = new
var/datum/action/innate/set_droppoint/set_droppoint_action = new
var/obj/machinery/abductor/console/console
lock_override = TRUE
icon = 'icons/obj/abductor.dmi'
icon_state = "camera"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
..()
eyeobj.visible_icon = TRUE
eyeobj.icon = 'icons/mob/cameramob.dmi'
eyeobj.icon_state = "generic_camera"
/obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user)
..()
if(tele_in_action)
tele_in_action.target = console.pad
tele_in_action.Grant(user)
actions += tele_in_action
if(tele_out_action)
tele_out_action.target = console
tele_out_action.Grant(user)
actions += tele_out_action
if(tele_self_action)
tele_self_action.target = console.pad
tele_self_action.Grant(user)
actions += tele_self_action
if(vest_mode_action)
vest_mode_action.target = console
vest_mode_action.Grant(user)
actions += vest_mode_action
if(vest_disguise_action)
vest_disguise_action.target = console
vest_disguise_action.Grant(user)
actions += vest_disguise_action
if(set_droppoint_action)
set_droppoint_action.target = console
set_droppoint_action.Grant(user)
actions += set_droppoint_action
/obj/machinery/computer/camera_advanced/abductor/proc/IsScientist(mob/living/carbon/human/H)
return HAS_TRAIT(H, TRAIT_ABDUCTOR_SCIENTIST_TRAINING)
/datum/action/innate/teleport_in
name = "Send To"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "beam_down"
/datum/action/innate/teleport_in/Activate()
if(!target || !iscarbon(owner))
return
var/mob/living/carbon/human/C = owner
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/pad/P = target
if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
P.PadToLoc(remote_eye.loc)
/datum/action/innate/teleport_out
name = "Retrieve"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "beam_up"
/datum/action/innate/teleport_out/Activate()
if(!target || !iscarbon(owner))
return
var/obj/machinery/abductor/console/console = target
console.TeleporterRetrieve()
/datum/action/innate/teleport_self
name = "Send Self"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "beam_down"
/datum/action/innate/teleport_self/Activate()
if(!target || !iscarbon(owner))
return
var/mob/living/carbon/human/C = owner
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/pad/P = target
if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
P.MobToLoc(remote_eye.loc,C)
/datum/action/innate/vest_mode_swap
name = "Switch Vest Mode"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "vest_mode"
/datum/action/innate/vest_mode_swap/Activate()
if(!target || !iscarbon(owner))
return
var/obj/machinery/abductor/console/console = target
console.FlipVest()
/datum/action/innate/vest_disguise_swap
name = "Switch Vest Disguise"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "vest_disguise"
/datum/action/innate/vest_disguise_swap/Activate()
if(!target || !iscarbon(owner))
return
var/obj/machinery/abductor/console/console = target
console.SelectDisguise(remote=1)
/datum/action/innate/set_droppoint
name = "Set Experiment Release Point"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "set_drop"
/datum/action/innate/set_droppoint/Activate()
if(!target || !iscarbon(owner))
return
var/mob/living/carbon/human/C = owner
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/console/console = target
console.SetDroppoint(remote_eye.loc,owner)
@@ -1,239 +0,0 @@
/proc/get_abductor_console(team_number)
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.team_number == team_number)
return C
//Common
/obj/machinery/abductor
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/team_number = 0
//Console
/obj/machinery/abductor/console
name = "abductor console"
desc = "Ship command center."
icon = 'icons/obj/abductor.dmi'
icon_state = "console"
density = TRUE
var/obj/item/abductor/gizmo/gizmo
var/obj/item/clothing/suit/armor/abductor/vest/vest
var/obj/machinery/abductor/experiment/experiment
var/obj/machinery/abductor/pad/pad
var/obj/machinery/computer/camera_advanced/abductor/camera
var/list/datum/icon_snapshot/disguises = list()
/obj/machinery/abductor/console/attack_hand(mob/user)
. = ..()
if(.)
return
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "<span class='warning'>You start mashing alien buttons at random!</span>")
if(do_after(user,100, target = src))
TeleporterSend()
return
user.set_machine(src)
var/dat = ""
dat += "<H3> Abductsoft 3000 </H3>"
if(experiment)
var/points = experiment.points
var/credits = experiment.credits
dat += "Collected Samples : [points] <br>"
dat += "Gear Credits: [credits] <br>"
dat += "<b>Transfer data in exchange for supplies:</b><br>"
dat += "<a href='?src=[REF(src)];dispense=baton'>Advanced Baton</A><br>"
dat += "<a href='?src=[REF(src)];dispense=helmet'>Agent Helmet</A><br>"
dat += "<a href='?src=[REF(src)];dispense=vest'>Agent Vest</A><br>"
dat += "<a href='?src=[REF(src)];dispense=silencer'>Radio Silencer</A><br>"
dat += "<a href='?src=[REF(src)];dispense=tool'>Science Tool</A><br>"
dat += "<a href='?src=[REF(src)];dispense=mind_device'>Mental Interface Device</A><br>"
else
dat += "<span class='bad'>NO EXPERIMENT MACHINE DETECTED</span> <br>"
if(pad)
dat += "<span class='bad'>Emergency Teleporter System.</span>"
dat += "<span class='bad'>Consider using primary observation console first.</span>"
dat += "<a href='?src=[REF(src)];teleporter_send=1'>Activate Teleporter</A><br>"
if(gizmo && gizmo.marked)
dat += "<a href='?src=[REF(src)];teleporter_retrieve=1'>Retrieve Mark</A><br>"
else
dat += "<span class='linkOff'>Retrieve Mark</span><br>"
else
dat += "<span class='bad'>NO TELEPAD DETECTED</span></br>"
if(vest)
dat += "<h4> Agent Vest Mode </h4><br>"
var/mode = vest.mode
if(mode == VEST_STEALTH)
dat += "<a href='?src=[REF(src)];flip_vest=1'>Combat</A>"
dat += "<span class='linkOff'>Stealth</span>"
else
dat += "<span class='linkOff'>Combat</span>"
dat += "<a href='?src=[REF(src)];flip_vest=1'>Stealth</A>"
dat+="<br>"
dat += "<a href='?src=[REF(src)];select_disguise=1'>Select Agent Vest Disguise</a><br>"
dat += "<a href='?src=[REF(src)];toggle_vest=1'>[HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "Unlock" : "Lock"] Vest</a><br>"
else
dat += "<span class='bad'>NO AGENT VEST DETECTED</span>"
var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500)
popup.set_content(dat)
popup.open()
/obj/machinery/abductor/console/Topic(href, href_list)
if(..())
return
usr.set_machine(src)
if(href_list["teleporter_send"])
TeleporterSend()
else if(href_list["teleporter_retrieve"])
TeleporterRetrieve()
else if(href_list["flip_vest"])
FlipVest()
else if(href_list["toggle_vest"])
if(vest)
vest.toggle_nodrop()
else if(href_list["select_disguise"])
SelectDisguise()
else if(href_list["dispense"])
switch(href_list["dispense"])
if("baton")
Dispense(/obj/item/abductor_baton,cost=2)
if("helmet")
Dispense(/obj/item/clothing/head/helmet/abductor)
if("silencer")
Dispense(/obj/item/abductor/silencer)
if("tool")
Dispense(/obj/item/abductor/gizmo)
if("vest")
Dispense(/obj/item/clothing/suit/armor/abductor/vest)
if("mind_device")
Dispense(/obj/item/abductor/mind_device,cost=2)
updateUsrDialog()
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
if(pad && gizmo && gizmo.marked)
pad.Retrieve(gizmo.marked)
/obj/machinery/abductor/console/proc/TeleporterSend()
if(pad)
pad.Send()
/obj/machinery/abductor/console/proc/FlipVest()
if(vest)
vest.flip_mode()
/obj/machinery/abductor/console/proc/SelectDisguise(remote = FALSE)
var/list/disguises2 = list()
for(var/name in disguises)
var/datum/icon_snapshot/snap = disguises[name]
var/image/dummy = image(snap.icon, src, snap.icon_state)
dummy.overlays = snap.overlays
disguises2[name] = dummy
var/entry_name
if(remote)
entry_name = show_radial_menu(usr, camera.eyeobj, disguises2)
else
entry_name = show_radial_menu(usr, src, disguises2)
var/datum/icon_snapshot/chosen = disguises[entry_name]
if(chosen && vest && (remote || in_range(usr,src)))
vest.SetDisguise(chosen)
/obj/machinery/abductor/console/proc/SetDroppoint(turf/open/location,user)
if(!istype(location))
to_chat(user, "<span class='warning'>That place is not safe for the specimen.</span>")
return
if(pad)
pad.teleport_target = location
to_chat(user, "<span class='notice'>Location marked as test subject release point.</span>")
/obj/machinery/abductor/console/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
/obj/machinery/abductor/console/LateInitialize()
if(!team_number)
return
for(var/obj/machinery/abductor/pad/p in GLOB.machines)
if(p.team_number == team_number)
pad = p
break
for(var/obj/machinery/abductor/experiment/e in GLOB.machines)
if(e.team_number == team_number)
experiment = e
e.console = src
for(var/obj/machinery/computer/camera_advanced/abductor/c in GLOB.machines)
if(c.team_number == team_number)
camera = c
c.console = src
/obj/machinery/abductor/console/proc/AddSnapshot(mob/living/carbon/human/target)
if(target.anti_magic_check(FALSE, FALSE, TRUE, 0))
say("Subject wearing specialized protective tinfoil gear, unable to get a proper scan!")
return
var/datum/icon_snapshot/entry = new
entry.name = target.name
entry.icon = target.icon
entry.icon_state = target.icon_state
entry.overlays = target.get_overlays_copy(list(HANDS_LAYER)) //ugh
//Update old disguise instead of adding new one
if(disguises[entry.name])
disguises[entry.name] = entry
return
disguises[entry.name] = entry
/obj/machinery/abductor/console/proc/AddGizmo(obj/item/abductor/gizmo/G)
if(G == gizmo && G.console == src)
return FALSE
if(G.console)
G.console.gizmo = null
gizmo = G
G.console = src
return TRUE
/obj/machinery/abductor/console/proc/AddVest(obj/item/clothing/suit/armor/abductor/vest/V)
if(vest == V)
return FALSE
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.vest == V)
C.vest = null
break
vest = V
return TRUE
/obj/machinery/abductor/console/attackby(obj/O, mob/user, params)
if(istype(O, /obj/item/abductor/gizmo) && AddGizmo(O))
to_chat(user, "<span class='notice'>You link the tool to the console.</span>")
else if(istype(O, /obj/item/clothing/suit/armor/abductor/vest) && AddVest(O))
to_chat(user, "<span class='notice'>You link the vest to the console.</span>")
else
return ..()
/obj/machinery/abductor/console/proc/Dispense(item,cost=1)
if(experiment && experiment.credits >= cost)
experiment.credits -=cost
say("Incoming supply!")
var/drop_location = loc
if(pad)
flick("alien-pad", pad)
drop_location = pad.loc
new item(drop_location)
else
say("Insufficent data!")
@@ -1,56 +0,0 @@
/obj/machinery/abductor/pad
name = "Alien Telepad"
desc = "Use this to transport to and from the humans' habitat."
icon = 'icons/obj/abductor.dmi'
icon_state = "alien-pad-idle"
var/turf/teleport_target
/obj/machinery/abductor/pad/proc/Warp(mob/living/target)
if(!target.buckled)
target.forceMove(get_turf(src))
/obj/machinery/abductor/pad/proc/Send()
if(teleport_target == null)
teleport_target = GLOB.teleportlocs[pick(GLOB.teleportlocs)]
flick("alien-pad", src)
for(var/mob/living/target in loc)
target.forceMove(teleport_target)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)
to_chat(target, "<span class='warning'>The instability of the warp leaves you disoriented!</span>")
target.Stun(60)
/obj/machinery/abductor/pad/proc/Retrieve(mob/living/target)
flick("alien-pad", src)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)
Warp(target)
/obj/machinery/abductor/pad/proc/doMobToLoc(place, atom/movable/target)
flick("alien-pad", src)
target.forceMove(place)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)
/obj/machinery/abductor/pad/proc/MobToLoc(place,mob/living/target)
new /obj/effect/temp_visual/teleport_abductor(place)
addtimer(CALLBACK(src, .proc/doMobToLoc, place, target), 80)
/obj/machinery/abductor/pad/proc/doPadToLoc(place)
flick("alien-pad", src)
for(var/mob/living/target in get_turf(src))
target.forceMove(place)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)
/obj/machinery/abductor/pad/proc/PadToLoc(place)
new /obj/effect/temp_visual/teleport_abductor(place)
addtimer(CALLBACK(src, .proc/doPadToLoc, place), 80)
/obj/effect/temp_visual/teleport_abductor
name = "Huh"
icon = 'icons/obj/abductor.dmi'
icon_state = "teleport"
duration = 80
/obj/effect/temp_visual/teleport_abductor/Initialize()
. = ..()
var/datum/effect_system/spark_spread/S = new
S.set_up(10,0,loc)
S.start()
-66
View File
@@ -1,66 +0,0 @@
/datum/antagonist/blob
name = "Blob"
roundend_category = "blobs"
antagpanel_category = "Blob"
job_rank = ROLE_BLOB
var/datum/action/innate/blobpop/pop_action
var/starting_points_human_blob = 60
var/point_rate_human_blob = 2
/datum/antagonist/blob/roundend_report()
var/basic_report = ..()
//Display max blobpoints for blebs that lost
if(isovermind(owner.current)) //embarrasing if not
var/mob/camera/blob/overmind = owner.current
if(!overmind.victory_in_progress) //if it won this doesn't really matter
var/point_report = "<br><b>[owner.name]</b> took over [overmind.max_count] tiles at the height of its growth."
return basic_report+point_report
return basic_report
/datum/antagonist/blob/greet()
if(!isovermind(owner.current))
to_chat(owner,"<span class='userdanger'>You feel bloated.</span>")
/datum/antagonist/blob/on_gain()
create_objectives()
. = ..()
/datum/antagonist/blob/proc/create_objectives()
var/datum/objective/blob_takeover/main = new
main.owner = owner
objectives += main
/datum/antagonist/blob/apply_innate_effects(mob/living/mob_override)
if(!isovermind(owner.current))
if(!pop_action)
pop_action = new
pop_action.Grant(owner.current)
/datum/objective/blob_takeover
explanation_text = "Reach critical mass!"
//Non-overminds get this on blob antag assignment
/datum/action/innate/blobpop
name = "Pop"
desc = "Unleash the blob"
icon_icon = 'icons/mob/blob.dmi'
button_icon_state = "blob"
/datum/action/innate/blobpop/Activate()
var/mob/old_body = owner
var/datum/antagonist/blob/blobtag = owner.mind.has_antag_datum(/datum/antagonist/blob)
if(!blobtag)
Remove()
return
var/mob/camera/blob/B = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob)
owner.mind.transfer_to(B)
old_body.gib()
B.place_blob_core(blobtag.point_rate_human_blob, pop_override = TRUE)
/datum/antagonist/blob/antag_listing_status()
. = ..()
if(owner && owner.current)
var/mob/camera/blob/B = owner.current
if(istype(B))
. += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
@@ -1,88 +0,0 @@
/obj/structure/blob/core
name = "blob core"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A huge, pulsating yellow mass."
max_integrity = 400
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 5, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) // Last stand
explosion_block = 6
point_return = -1
health_regen = 0 //we regen in Life() instead of when pulsed
var/core_regen = 2
var/resource_delay = 0
var/point_rate = 2
/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, new_rate = 2, placed = 0)
GLOB.blob_cores += src
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
update_icon() //so it atleast appears
if(!placed && !overmind)
return INITIALIZE_HINT_QDEL
if(overmind)
update_icon()
point_rate = new_rate
addtimer(CALLBACK(src, .proc/generate_announcement), 1800)
. = ..()
/obj/structure/blob/core/proc/generate_announcement()
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", "outbreak5")
/obj/structure/blob/core/scannerreport()
return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
/obj/structure/blob/core/update_icon()
cut_overlays()
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blob_reagent_datum.color
add_overlay(blob_overlay)
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay"))
/obj/structure/blob/core/Destroy()
GLOB.blob_cores -= src
if(overmind)
overmind.blob_core = null
overmind = null
STOP_PROCESSING(SSobj, src)
GLOB.poi_list -= src
return ..()
/obj/structure/blob/core/ex_act(severity, target)
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
take_damage(damage, BRUTE, "bomb", 0)
/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1)
. = ..()
if(obj_integrity > 0)
if(overmind) //we should have an overmind, but...
overmind.update_health_hud()
/obj/structure/blob/core/Life()
if(QDELETED(src))
return
if(!overmind)
qdel(src)
else
if(resource_delay <= world.time)
resource_delay = world.time + 10 // 1 second
overmind.add_points(point_rate)
obj_integrity = min(max_integrity, obj_integrity+core_regen)
if(overmind)
overmind.update_health_hud()
Pulse_Area(overmind, 12, 4, 3)
for(var/obj/structure/blob/normal/B in range(1, src))
if(prob(5))
B.change_to(/obj/structure/blob/shield/core, overmind)
..()
/obj/structure/blob/core/ComponentInitialize()
. = ..()
AddComponent(/datum/component/stationloving, FALSE, TRUE)
/obj/structure/blob/core/onTransitZ(old_z, new_z)
if(overmind && is_station_level(new_z))
overmind.forceMove(get_turf(src))
return ..()
@@ -1,258 +0,0 @@
//Few global vars to track the blob
GLOBAL_LIST_EMPTY(blobs) //complete list of all blobs made.
GLOBAL_LIST_EMPTY(blob_cores)
GLOBAL_LIST_EMPTY(overminds)
GLOBAL_LIST_EMPTY(blob_nodes)
/mob/camera/blob
name = "Blob Overmind"
real_name = "Blob Overmind"
desc = "The overmind. It controls the blob."
icon = 'icons/mob/cameramob.dmi'
icon_state = "marker"
mouse_opacity = MOUSE_OPACITY_ICON
move_on_shuttle = 1
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = FLY_LAYER
pass_flags = PASSBLOB
faction = list(ROLE_BLOB)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
call_life = TRUE
hud_type = /datum/hud/blob_overmind
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 100
var/last_attack = 0
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
var/list/blob_mobs = list()
var/list/resource_blobs = list()
var/free_chem_rerolls = 1 //one free chemical reroll
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
var/placed = 0
var/base_point_rate = 2 //for blob core placement
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
var/list/blobs_legit = list()
var/max_count = 0 //The biggest it got before death
var/blobwincount = 400
var/victory_in_progress = FALSE
/mob/camera/blob/Initialize(mapload, starting_points = 60)
validate_location()
blob_points = starting_points
manualplace_min_time += world.time
autoplace_max_time += world.time
GLOB.overminds += src
var/new_name = "[initial(name)] ([rand(1, 999)])"
name = new_name
real_name = new_name
last_attack = world.time
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob)))
blob_reagent_datum = new BC
color = blob_reagent_datum.complementary_color
if(blob_core)
blob_core.update_icon()
SSshuttle.registerHostileEnvironment(src)
.= ..()
/mob/camera/blob/proc/validate_location()
var/turf/T = get_turf(src)
if(!is_valid_turf(T) && LAZYLEN(GLOB.blobstart))
var/list/blobstarts = shuffle(GLOB.blobstart)
for(var/_T in blobstarts)
if(is_valid_turf(_T))
T = _T
break
if(!T)
CRASH("No blobspawnpoints and blob spawned in nullspace.")
forceMove(T)
/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))
return FALSE
return TRUE
/mob/camera/blob/Life()
if(!blob_core)
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
to_chat(src, "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
place_blob_core(base_point_rate, 1)
else
qdel(src)
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
victory_in_progress = TRUE
priority_announce("Biohazard has reached critical mass. Station loss is imminent.", "Biohazard Alert")
set_security_level("delta")
max_blob_points = INFINITY
blob_points = INFINITY
addtimer(CALLBACK(src, .proc/victory), 450)
if(!victory_in_progress && max_count < blobs_legit.len)
max_count = blobs_legit.len
..()
/mob/camera/blob/proc/victory()
sound_to_playing_players('sound/machines/alarm.ogg')
sleep(100)
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
var/turf/T = get_turf(L)
if(!T || !is_station_level(T.z))
continue
if(L in GLOB.overminds || (L.pass_flags & PASSBLOB))
continue
var/area/Ablob = get_area(T)
if(!Ablob.blob_allowed)
continue
if(!(ROLE_BLOB in L.faction))
playsound(L, 'sound/effects/splat.ogg', 50, 1)
L.death()
new/mob/living/simple_animal/hostile/blob/blobspore(T)
else
L.fully_heal()
for(var/area/A in GLOB.sortedAreas)
if(!(A.type in GLOB.the_station_areas))
continue
if(!A.blob_allowed)
continue
A.color = blob_reagent_datum.color
A.name = "blob"
A.icon = 'icons/mob/blob.dmi'
A.icon_state = "blob_shield"
A.layer = BELOW_MOB_LAYER
A.invisibility = 0
A.blend_mode = 0
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(B)
var/datum/objective/blob_takeover/main_objective = locate() in B.objectives
if(main_objective)
main_objective.completed = TRUE
to_chat(world, "<B>[real_name] consumed the station in an unstoppable tide!</B>")
SSticker.news_report = BLOB_WIN
SSticker.force_ending = 1
/mob/camera/blob/Destroy()
for(var/BL in GLOB.blobs)
var/obj/structure/blob/B = BL
if(B && B.overmind == src)
B.overmind = null
B.update_icon() //reset anything that was ours
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
if(BM)
BM.overmind = null
BM.update_icons()
GLOB.overminds -= src
SSshuttle.clearHostileEnvironment(src)
return ..()
/mob/camera/blob/Login()
..()
to_chat(src, "<span class='notice'>You are the overmind!</span>")
blob_help()
update_health_hud()
add_points(0)
/mob/camera/blob/examine(mob/user)
..()
if(blob_reagent_datum)
to_chat(user, "Its chemical is <font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>.")
/mob/camera/blob/update_health_hud()
if(blob_core)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_core.obj_integrity)]</font></div>"
for(var/mob/living/simple_animal/hostile/blob/blobbernaut/B in blob_mobs)
if(B.hud_used && B.hud_used.blobpwrdisplay)
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.obj_integrity)]</font></div>"
/mob/camera/blob/proc/add_points(points)
blob_points = CLAMP(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_points)]</font></div>"
/mob/camera/blob/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if (!message)
return
if (src.client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
return
if (src.client.handle_spam_prevention(message,MUTE_IC))
return
if (stat)
return
blob_talk(message)
/mob/camera/blob/proc/blob_talk(message)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
src.log_talk(message, LOG_SAY)
var/message_a = say_quote(message)
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>)</b> [message_a]</font></span>"
for(var/mob/M in GLOB.mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
to_chat(M, rendered)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [rendered]")
/mob/camera/blob/blob_act(obj/structure/blob/B)
return
/mob/camera/blob/Stat()
..()
if(statpanel("Status"))
if(blob_core)
stat(null, "Core Health: [blob_core.obj_integrity]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
stat(null, "Blobs to Win: [blobs_legit.len]/[blobwincount]")
if(free_chem_rerolls)
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
if(!placed)
if(manualplace_min_time)
stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
stat(null, "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]")
/mob/camera/blob/Move(NewLoc, Dir = 0)
if(placed)
var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
if(B)
forceMove(NewLoc)
else
return 0
else
var/area/A = get_area(NewLoc)
if(isspaceturf(NewLoc) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
return 0
forceMove(NewLoc)
return 1
/mob/camera/blob/mind_initialize()
. = ..()
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(!B)
mind.add_antag_datum(/datum/antagonist/blob)
@@ -1,75 +0,0 @@
/proc/brainwash(mob/living/L, directives)
if(!L.mind)
return
if(!islist(directives))
directives = list(directives)
var/datum/mind/M = L.mind
var/datum/antagonist/brainwashed/B = M.has_antag_datum(/datum/antagonist/brainwashed)
if(B)
for(var/O in directives)
var/datum/objective/brainwashing/objective = new(O)
B.objectives += objective
B.greet()
else
B = new()
for(var/O in directives)
var/datum/objective/brainwashing/objective = new(O)
B.objectives += objective
M.add_antag_datum(B)
var/begin_message = "<span class='deadsay'><b>[L]</b> has been brainwashed with the following objectives: "
var/obj_message = english_list(directives)
var/end_message = "</b>.</span>"
var/rendered = begin_message + obj_message + end_message
deadchat_broadcast(rendered, follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_REGULAR)
/datum/antagonist/brainwashed
name = "Brainwashed Victim"
job_rank = ROLE_BRAINWASHED
roundend_category = "brainwashed victims"
show_in_antagpanel = TRUE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
/datum/antagonist/brainwashed/greet()
to_chat(owner, "<span class='warning'>Your mind reels as it begins focusing on a single purpose...</span>")
to_chat(owner, "<big><span class='warning'><b>Follow the Directives, at any cost!</b></span></big>")
var/i = 1
for(var/X in objectives)
var/datum/objective/O = X
to_chat(owner, "<b>[i].</b> [O.explanation_text]")
i++
/datum/antagonist/brainwashed/farewell()
to_chat(owner, "<span class='warning'>Your mind suddenly clears...</span>")
to_chat(owner, "<big><span class='warning'><b>You feel the weight of the Directives disappear! You no longer have to obey them.</b></span></big>")
owner.announce_objectives()
/datum/antagonist/brainwashed/admin_add(datum/mind/new_owner,mob/admin)
var/mob/living/carbon/C = new_owner.current
if(!istype(C))
return
var/list/objectives = list()
do
var/objective = stripped_input(admin, "Add an objective, or leave empty to finish.", "Brainwashing", null, MAX_MESSAGE_LEN)
if(objective)
objectives += objective
while(alert(admin,"Add another objective?","More Brainwashing","Yes","No") == "Yes")
if(alert(admin,"Confirm Brainwashing?","Are you sure?","Yes","No") == "No")
return
if(!LAZYLEN(objectives))
return
if(QDELETED(C))
to_chat(admin, "Mob doesn't exist anymore")
return
brainwash(C, objectives)
var/obj_list = english_list(objectives)
message_admins("[key_name_admin(admin)] has brainwashed [key_name_admin(C)] with the following objectives: [obj_list].")
log_admin("[key_name(admin)] has brainwashed [key_name(C)] with the following objectives: [obj_list].")
/datum/objective/brainwashing
completed = TRUE
-154
View File
@@ -1,154 +0,0 @@
/datum/antagonist/brother
name = "Brother"
antagpanel_category = "Brother"
job_rank = ROLE_BROTHER
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_HIJACKER
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
team = new_team
/datum/antagonist/brother/get_team()
return team
/datum/antagonist/brother/on_gain()
SSticker.mode.brothers += owner
objectives += team.objectives
owner.special_role = special_role
finalize_brother()
return ..()
/datum/antagonist/brother/on_removal()
SSticker.mode.brothers -= owner
if(owner.current)
to_chat(owner.current,"<span class='userdanger'>You are no longer the [special_role]!</span>")
owner.special_role = null
return ..()
/datum/antagonist/brother/proc/give_meeting_area()
if(!owner.current || !team || !team.meeting_area)
return
to_chat(owner.current, "<B>Your designated meeting area:</B> [team.meeting_area]")
antag_memory += "<b>Meeting Area</b>: [team.meeting_area]<br>"
/datum/antagonist/brother/greet()
var/brother_text = ""
var/list/brothers = team.members - owner
for(var/i = 1 to brothers.len)
var/datum/mind/M = brothers[i]
brother_text += M.name
if(i == brothers.len - 1)
brother_text += " and "
else if(i != brothers.len)
brother_text += ", "
to_chat(owner.current, "<B><font size=3 color=red>You are the [owner.special_role] of [brother_text].</font></B>")
to_chat(owner.current, "The Syndicate only accepts those that have proven themselves. Prove yourself and prove your [team.member_name]s by completing your objectives together!")
owner.announce_objectives()
give_meeting_area()
/datum/antagonist/brother/proc/finalize_brother()
SSticker.mode.update_brother_icons_added(owner)
/datum/antagonist/brother/admin_add(datum/mind/new_owner,mob/admin)
//show list of possible brothers
var/list/candidates = list()
for(var/mob/living/L in GLOB.alive_mob_list)
if(!L.mind || L.mind == new_owner || !can_be_owned(L.mind))
continue
candidates[L.mind.name] = L.mind
var/choice = input(admin,"Choose the blood brother.", "Brother") as null|anything in candidates
if(!choice)
return
var/datum/mind/bro = candidates[choice]
var/datum/team/brother_team/T = new
T.add_member(new_owner)
T.add_member(bro)
T.pick_meeting_area()
T.forge_brother_objectives()
new_owner.add_antag_datum(/datum/antagonist/brother,T)
bro.add_antag_datum(/datum/antagonist/brother, T)
T.update_name()
message_admins("[key_name_admin(admin)] made [new_owner.current] and [bro.current] into blood brothers.")
log_admin("[key_name(admin)] made [new_owner.current] and [bro.current] into blood brothers.")
/datum/team/brother_team
name = "brotherhood"
member_name = "blood brother"
var/meeting_area
var/static/meeting_areas = list("The Bar", "Dorms", "Escape Dock", "Arrivals", "Holodeck", "Primary Tool Storage", "Recreation Area", "Chapel", "Library")
/datum/team/brother_team/is_solo()
return FALSE
/datum/team/brother_team/proc/pick_meeting_area()
meeting_area = pick(meeting_areas)
meeting_areas -= meeting_area
/datum/team/brother_team/proc/update_name()
var/list/last_names = list()
for(var/datum/mind/M in members)
var/list/split_name = splittext(M.name," ")
last_names += split_name[split_name.len]
name = last_names.Join(" & ")
/datum/team/brother_team/roundend_report()
var/list/parts = list()
parts += "<span class='header'>The blood brothers of [name] were:</span>"
for(var/datum/mind/M in members)
parts += printplayer(M)
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
objective_count++
if(win)
parts += "<span class='greentext'>The blood brothers were successful!</span>"
else
parts += "<span class='redtext'>The blood brothers have failed!</span>"
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
/datum/team/brother_team/proc/add_objective(datum/objective/O, needs_target = FALSE)
O.team = src
if(needs_target)
O.find_target()
O.update_explanation_text()
objectives += O
/datum/team/brother_team/proc/forge_brother_objectives()
objectives = list()
var/is_hijacker = prob(10)
for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
forge_single_objective()
if(is_hijacker)
if(!locate(/datum/objective/hijack) in objectives)
add_objective(new/datum/objective/hijack)
else if(!locate(/datum/objective/escape) in objectives)
add_objective(new/datum/objective/escape)
/datum/team/brother_team/proc/forge_single_objective()
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
add_objective(new/datum/objective/steal, TRUE)
/datum/team/brother_team/antag_listing_name()
return "[name] blood brothers"
@@ -1,573 +0,0 @@
#define LING_FAKEDEATH_TIME 400 //40 seconds
#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead.
#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob
/datum/antagonist/changeling
name = "Changeling"
roundend_category = "changelings"
antagpanel_category = "Changeling"
job_rank = ROLE_CHANGELING
antag_moodlet = /datum/mood_event/focused
var/you_are_greet = TRUE
var/give_objectives = TRUE
var/team_mode = FALSE //Should assign team objectives ?
//Changeling Stuff
var/list/stored_profiles = list() //list of datum/changelingprofile
var/datum/changelingprofile/first_prof = null
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
var/absorbedcount = 0
var/trueabsorbs = 0//dna gained using absorb, not dna sting
var/chem_charges = 20
var/chem_storage = 75
var/chem_recharge_rate = 1
var/chem_recharge_slowdown = 0
var/sting_range = 2
var/changelingID = "Changeling"
var/geneticdamage = 0
var/isabsorbing = 0
var/islinking = 0
var/geneticpoints = 10
var/purchasedpowers = list()
var/mimicing = ""
var/canrespec = 0
var/changeling_speak = 0
var/loudfactor = 0 //Used for blood tests. At 4, blood tests will succeed. At 10, blood tests will result in an explosion.
var/bloodtestwarnings = 0 //Used to track if the ling has been notified that they will pass blood tests.
var/datum/dna/chosen_dna
var/obj/effect/proc_holder/changeling/sting/chosen_sting
var/datum/cellular_emporium/cellular_emporium
var/datum/action/innate/cellular_emporium/emporium_action
// wip stuff
var/static/list/all_powers = typecacheof(/obj/effect/proc_holder/changeling,TRUE)
/datum/antagonist/changeling/Destroy()
QDEL_NULL(cellular_emporium)
QDEL_NULL(emporium_action)
. = ..()
/datum/antagonist/changeling/proc/generate_name()
var/honorific
if(owner.current.gender == FEMALE)
honorific = "Ms."
else
honorific = "Mr."
if(GLOB.possible_changeling_IDs.len)
changelingID = pick(GLOB.possible_changeling_IDs)
GLOB.possible_changeling_IDs -= changelingID
changelingID = "[honorific] [changelingID]"
else
changelingID = "[honorific] [rand(1,999)]"
/datum/antagonist/changeling/proc/create_actions()
cellular_emporium = new(src)
emporium_action = new(cellular_emporium)
/datum/antagonist/changeling/on_gain()
generate_name()
create_actions()
reset_powers()
create_initial_profile()
if(give_objectives)
forge_objectives()
remove_clownmut()
. = ..()
/datum/antagonist/changeling/on_removal()
//We'll be using this from now on
var/mob/living/carbon/C = owner.current
if(istype(C))
var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN)
if(B && (B.decoy_override != initial(B.decoy_override)))
B.organ_flags |= ORGAN_VITAL
B.decoy_override = FALSE
remove_changeling_powers()
. = ..()
/datum/antagonist/changeling/proc/remove_clownmut()
if (owner)
var/mob/living/carbon/human/H = owner.current
if(istype(H) && owner.assigned_role == "Clown")
to_chat(H, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.")
H.dna.remove_mutation(CLOWNMUT)
/datum/antagonist/changeling/proc/reset_properties()
changeling_speak = 0
chosen_sting = null
geneticpoints = initial(geneticpoints)
sting_range = initial(sting_range)
chem_storage = initial(chem_storage)
chem_recharge_rate = initial(chem_recharge_rate)
chem_charges = min(chem_charges, chem_storage)
chem_recharge_slowdown = initial(chem_recharge_slowdown)
mimicing = ""
/datum/antagonist/changeling/proc/remove_changeling_powers()
if(ishuman(owner.current) || ismonkey(owner.current))
reset_properties()
for(var/obj/effect/proc_holder/changeling/p in purchasedpowers)
if(p.always_keep)
continue
purchasedpowers -= p
p.on_refund(owner.current)
//MOVE THIS
if(owner.current.hud_used)
owner.current.hud_used.lingstingdisplay.icon_state = null
owner.current.hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
/datum/antagonist/changeling/proc/reset_powers()
if(purchasedpowers)
remove_changeling_powers()
loudfactor = 0
bloodtestwarnings = 0
//Repurchase free powers.
for(var/path in all_powers)
var/obj/effect/proc_holder/changeling/S = new path()
if(!S.dna_cost)
if(!has_sting(S))
purchasedpowers += S
S.on_purchase(owner.current,TRUE)
/datum/antagonist/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
if(initial(power.name) == P.name)
return TRUE
return FALSE
/datum/antagonist/changeling/proc/purchase_power(sting_name)
var/obj/effect/proc_holder/changeling/thepower = null
for(var/path in all_powers)
var/obj/effect/proc_holder/changeling/S = path
if(initial(S.name) == sting_name)
thepower = new path()
break
if(!thepower)
to_chat(owner.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!")
return
if(absorbedcount < thepower.req_dna)
to_chat(owner.current, "We lack the energy to evolve this ability!")
return
if(has_sting(thepower))
to_chat(owner.current, "We have already evolved this ability!")
return
if(thepower.dna_cost < 0)
to_chat(owner.current, "We cannot evolve this ability.")
return
if(geneticpoints < thepower.dna_cost)
to_chat(owner.current, "We have reached our capacity for abilities.")
return
if(HAS_TRAIT(owner.current, TRAIT_DEATHCOMA))//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
to_chat(owner.current, "We lack the energy to evolve new abilities right now.")
return
geneticpoints -= thepower.dna_cost
purchasedpowers += thepower
thepower.on_purchase(owner.current)
loudfactor += thepower.loudness
if(loudfactor >= 4 && !bloodtestwarnings)
to_chat(owner.current, "<span class='warning'>Our blood is growing flammable. Our blood will react violently to heat.</span>")
bloodtestwarnings = 1
if(loudfactor >= 10 && bloodtestwarnings < 2)
to_chat(owner.current, "<span class='warning'>Our blood has grown extremely flammable. Our blood will react explosively to heat.</span>")
bloodtestwarnings = 2
/datum/antagonist/changeling/proc/readapt()
if(!ishuman(owner.current))
to_chat(owner.current, "<span class='danger'>We can't remove our evolutions in this form!</span>")
return
if(canrespec)
to_chat(owner.current, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
reset_powers()
playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5, soundenvwet = 0)
canrespec = 0
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
return 1
else
to_chat(owner.current, "<span class='danger'>You lack the power to readapt your evolutions!</span>")
return 0
//Called in life()
/datum/antagonist/changeling/proc/regenerate()
var/mob/living/carbon/the_ling = owner.current
if(istype(the_ling))
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)
else //not dead? no chem/geneticdamage caps.
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage)
geneticdamage = max(0, geneticdamage-1)
/datum/antagonist/changeling/proc/get_dna(dna_owner)
for(var/datum/changelingprofile/prof in stored_profiles)
if(dna_owner == prof.name)
return prof
/datum/antagonist/changeling/proc/has_dna(datum/dna/tDNA)
for(var/datum/changelingprofile/prof in stored_profiles)
if(tDNA.is_same_as(prof.dna))
return TRUE
return FALSE
/datum/antagonist/changeling/proc/can_absorb_dna(mob/living/carbon/human/target, var/verbose=1)
var/mob/living/carbon/user = owner.current
if(!istype(user))
return
if(stored_profiles.len)
var/datum/changelingprofile/prof = stored_profiles[1]
if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it.
if(verbose)
to_chat(user, "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>")
return
if(!target)
return
if(NO_DNA_COPY in target.dna.species.species_traits)
if(verbose)
to_chat(user, "<span class='warning'>[target] is not compatible with our biology.</span>")
return
if((HAS_TRAIT(target, TRAIT_NOCLONE)) || (HAS_TRAIT(target, TRAIT_NOCLONE)))
if(verbose)
to_chat(user, "<span class='warning'>DNA of [target] is ruined beyond usability!</span>")
return
if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway!
if(verbose)
to_chat(user, "<span class='warning'>We could gain no benefit from absorbing a lesser creature.</span>")
return
if(has_dna(target.dna))
if(verbose)
to_chat(user, "<span class='warning'>We already have this DNA in storage!</span>")
return
if(!target.has_dna())
if(verbose)
to_chat(user, "<span class='warning'>[target] is not compatible with our biology.</span>")
return
return 1
/datum/antagonist/changeling/proc/create_profile(mob/living/carbon/human/H, protect = 0)
var/datum/changelingprofile/prof = new
H.dna.real_name = H.real_name //Set this again, just to be sure that it's properly set.
var/datum/dna/new_dna = new H.dna.type
H.dna.copy_dna(new_dna)
prof.dna = new_dna
prof.name = H.real_name
prof.protected = protect
prof.underwear = H.underwear
prof.undie_color = H.undie_color
prof.undershirt = H.undershirt
prof.shirt_color = H.shirt_color
prof.socks = H.socks
prof.socks_color = H.socks_color
var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")
for(var/slot in slots)
if(slot in H.vars)
var/obj/item/I = H.vars[slot]
if(!I)
continue
prof.name_list[slot] = I.name
prof.appearance_list[slot] = I.appearance
prof.flags_cover_list[slot] = I.flags_cover
prof.item_color_list[slot] = I.item_color
prof.item_state_list[slot] = I.item_state
prof.exists_list[slot] = 1
else
continue
return prof
/datum/antagonist/changeling/proc/add_profile(datum/changelingprofile/prof)
if(stored_profiles.len > dna_max)
if(!push_out_profile())
return
if(!first_prof)
first_prof = prof
stored_profiles += prof
absorbedcount++
/datum/antagonist/changeling/proc/add_new_profile(mob/living/carbon/human/H, protect = 0)
var/datum/changelingprofile/prof = create_profile(H, protect)
add_profile(prof)
return prof
/datum/antagonist/changeling/proc/remove_profile(mob/living/carbon/human/H, force = 0)
for(var/datum/changelingprofile/prof in stored_profiles)
if(H.real_name == prof.name)
if(prof.protected && !force)
continue
stored_profiles -= prof
qdel(prof)
/datum/antagonist/changeling/proc/get_profile_to_remove()
for(var/datum/changelingprofile/prof in stored_profiles)
if(!prof.protected)
return prof
/datum/antagonist/changeling/proc/push_out_profile()
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
if(removeprofile)
stored_profiles -= removeprofile
return 1
return 0
/datum/antagonist/changeling/proc/create_initial_profile()
var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste
if(ishuman(C))
add_new_profile(C)
/datum/antagonist/changeling/apply_innate_effects()
//Brains optional.
var/mob/living/carbon/C = owner.current
if(istype(C))
var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN)
if(B)
B.organ_flags &= ~ORGAN_VITAL
B.decoy_override = TRUE
update_changeling_icons_added()
return
/datum/antagonist/changeling/remove_innate_effects()
update_changeling_icons_removed()
return
/datum/antagonist/changeling/greet()
if (you_are_greet)
to_chat(owner.current, "<span class='boldannounce'>You are [changelingID], a changeling! You have absorbed and taken the form of a human.</span>")
to_chat(owner.current, "<span class='boldannounce'>Use say \"[MODE_TOKEN_CHANGELING] message\" to communicate with your fellow changelings.</span>")
to_chat(owner.current, "<b>You must complete the following tasks:</b>")
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ling_aler.ogg', 100, FALSE, pressure_affected = FALSE)
owner.announce_objectives()
/datum/antagonist/changeling/farewell()
to_chat(owner.current, "<span class='userdanger'>You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!</span>")
/datum/antagonist/changeling/proc/forge_team_objectives()
if(GLOB.changeling_team_objective_type)
var/datum/objective/changeling_team_objective/team_objective = new GLOB.changeling_team_objective_type
team_objective.owner = owner
if(team_objective.prepare())//Setting up succeeded
objectives += team_objective
else
qdel(team_objective)
return
/datum/antagonist/changeling/proc/forge_objectives()
//OBJECTIVES - random traitor objectives. Unique objectives "steal brain" and "identity theft".
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
var/escape_objective_possible = TRUE
//if there's a team objective, check if it's compatible with escape objectives
for(var/datum/objective/changeling_team_objective/CTO in objectives)
if(!CTO.escape_objective_compatible)
escape_objective_possible = FALSE
break
var/changeling_objective = rand(1,3)
switch(changeling_objective)
if(1)
var/datum/objective/absorb/absorb_objective = new
absorb_objective.owner = owner
absorb_objective.gen_amount_goal(6, 8)
objectives += absorb_objective
if(2)
var/datum/objective/absorb_changeling/ac = new
ac.owner = owner
objectives += ac
if(3)
var/datum/objective/absorb_most/ac = new
ac.owner = owner
objectives += ac
if(prob(60))
if(prob(85))
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
objectives += steal_objective
else
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
objectives += download_objective
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
destroy_objective.owner = owner
destroy_objective.find_target()
objectives += destroy_objective
else
if(prob(70))
var/datum/objective/assassinate/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
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
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)
if(prob(50))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = owner
objectives += escape_objective
else
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = owner
if(team_mode)
identity_theft.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
else
identity_theft.find_target()
objectives += identity_theft
escape_objective_possible = FALSE
/datum/antagonist/changeling/proc/update_changeling_icons_added()
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING]
hud.join_hud(owner.current)
set_antag_hud(owner.current, "changeling")
/datum/antagonist/changeling/proc/update_changeling_icons_removed()
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING]
hud.leave_hud(owner.current)
set_antag_hud(owner.current, null)
/datum/antagonist/changeling/admin_add(datum/mind/new_owner,mob/admin)
. = ..()
to_chat(new_owner.current, "<span class='boldannounce'>Our powers have awoken. A flash of memory returns to us...we are [changelingID], a changeling!</span>")
/datum/antagonist/changeling/get_admin_commands()
. = ..()
if(stored_profiles.len && (owner.current.real_name != first_prof.name))
.["Transform to initial appearance."] = CALLBACK(src,.proc/admin_restore_appearance)
/datum/antagonist/changeling/proc/admin_restore_appearance(mob/admin)
if(!stored_profiles.len || !iscarbon(owner.current))
to_chat(admin, "<span class='danger'>Resetting DNA failed!</span>")
else
var/mob/living/carbon/C = owner.current
first_prof.dna.transfer_identity(C, transfer_SE=1)
C.real_name = first_prof.name
C.updateappearance(mutcolor_update=1)
C.domutcheck()
// Profile
/datum/changelingprofile
var/name = "a bug"
var/protected = 0
var/datum/dna/dna = null
var/list/name_list = list() //associative list of slotname = itemname
var/list/appearance_list = list()
var/list/flags_cover_list = list()
var/list/exists_list = list()
var/list/item_color_list = list()
var/list/item_state_list = list()
var/underwear
var/undie_color
var/undershirt
var/shirt_color
var/socks
var/socks_color
/datum/changelingprofile/Destroy()
qdel(dna)
. = ..()
/datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile)
newprofile.name = name
newprofile.protected = protected
newprofile.dna = new dna.type
dna.copy_dna(newprofile.dna)
newprofile.name_list = name_list.Copy()
newprofile.appearance_list = appearance_list.Copy()
newprofile.flags_cover_list = flags_cover_list.Copy()
newprofile.exists_list = exists_list.Copy()
newprofile.item_color_list = item_color_list.Copy()
newprofile.item_state_list = item_state_list.Copy()
newprofile.underwear = underwear
newprofile.undershirt = undershirt
newprofile.socks = socks
/datum/antagonist/changeling/xenobio
name = "Xenobio Changeling"
give_objectives = FALSE
show_in_roundend = FALSE //These are here for admin tracking purposes only
you_are_greet = FALSE
/datum/antagonist/changeling/roundend_report()
var/list/parts = list()
var/changelingwin = 1
if(!owner.current)
changelingwin = 0
parts += printplayer(owner)
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
parts += "<b>Changeling ID:</b> [changelingID]."
parts += "<b>Genomes Extracted:</b> [absorbedcount]"
parts += " "
if(objectives.len)
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = 0
count++
if(changelingwin)
parts += "<span class='greentext'>The changeling was successful!</span>"
else
parts += "<span class='redtext'>The changeling has failed.</span>"
return parts.Join("<br>")
/datum/antagonist/changeling/antag_listing_name()
return ..() + "([changelingID])"
/datum/antagonist/changeling/xenobio/antag_listing_name()
return ..() + "(Xenobio)"
@@ -1,566 +0,0 @@
/*
Changeling Mutations! ~By Miauw (ALL OF IT :V)
Contains:
Arm Blade
Space Suit
Shield
Armor
Tentacles
*/
//Parent to shields and blades because muh copypasted code.
/obj/effect/proc_holder/changeling/weapon
name = "Organic Weapon"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
var/silent = FALSE
var/weapon_type
var/weapon_name_simple
/obj/effect/proc_holder/changeling/weapon/try_to_sting(mob/user, mob/target)
for(var/obj/item/I in user.held_items)
if(check_weapon(user, I))
return
..(user, target)
/obj/effect/proc_holder/changeling/weapon/proc/check_weapon(mob/user, obj/item/hand_item)
if(istype(hand_item, weapon_type))
user.temporarilyRemoveItemFromInventory(hand_item, TRUE) //DROPDEL will delete the item
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms [user.p_their()] [weapon_name_simple] into an arm!</span>", "<span class='notice'>We assimilate the [weapon_name_simple] back into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
user.update_inv_hands()
return 1
/obj/effect/proc_holder/changeling/weapon/sting_action(mob/living/user)
var/obj/item/held = user.get_active_held_item()
if(held && !user.dropItemToGround(held))
to_chat(user, "<span class='warning'>[held] is stuck to your hand, you cannot grow a [weapon_name_simple] over it!</span>")
return
var/limb_regen = 0
if(user.active_hand_index % 2 == 0) //we regen the arm before changing it into the weapon
limb_regen = user.regenerate_limb(BODY_ZONE_R_ARM, 1)
else
limb_regen = user.regenerate_limb(BODY_ZONE_L_ARM, 1)
if(limb_regen)
user.visible_message("<span class='warning'>[user]'s missing arm reforms, making a loud, grotesque sound!</span>", "<span class='userdanger'>Your arm regrows, making a loud, crunchy sound and giving you great pain!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
user.emote("scream")
var/obj/item/W = new weapon_type(user, silent)
user.put_in_hands(W)
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
action.Remove(user)
for(var/obj/item/I in user.held_items)
check_weapon(user, I)
//Parent to space suits and armor.
/obj/effect/proc_holder/changeling/suit
name = "Organic Suit"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
var/helmet_type = /obj/item
var/suit_type = /obj/item
var/suit_name_simple = " "
var/helmet_name_simple = " "
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/suit/try_to_sting(mob/user, mob/target)
if(check_suit(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have an organic suit and casts it off.
/obj/effect/proc_holder/changeling/suit/proc/check_suit(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
H.update_inv_head()
H.update_hair()
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/suit/on_refund(mob/user)
if(!ishuman(user))
return
action.Remove(user)
var/mob/living/carbon/human/H = user
check_suit(H)
/obj/effect/proc_holder/changeling/suit/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.wear_suit))
to_chat(user, "\the [user.wear_suit] is stuck to your body, you cannot grow a [suit_name_simple] over it!")
return
if(!user.canUnEquip(user.head))
to_chat(user, "\the [user.head] is stuck on your head, you cannot grow a [helmet_name_simple] over it!")
return
user.dropItemToGround(user.head)
user.dropItemToGround(user.wear_suit)
user.equip_to_slot_if_possible(new suit_type(user), SLOT_WEAR_SUIT, 1, 1, 1)
user.equip_to_slot_if_possible(new helmet_type(user), SLOT_HEAD, 1, 1, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return TRUE
//fancy headers yo
/***************************************\
|***************ARM BLADE***************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/arm_blade
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
dna_cost = 2
loudness = 2
req_human = 1
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armblade"
action_background_icon_state = "bg_ling"
/obj/item/melee/arm_blade
name = "arm blade"
desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "arm_blade"
item_state = "arm_blade"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL
w_class = WEIGHT_CLASS_HUGE
force = 25
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
armour_penetration = 20
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = IS_SHARP
var/can_drop = FALSE
var/fake = FALSE
total_mass = TOTAL_MASS_HAND_REPLACEMENT
/obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
if(ismob(loc) && !silent)
loc.visible_message("<span class='warning'>A grotesque blade forms around [loc.name]\'s arm!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a deadly blade.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
if(synthetic)
can_drop = TRUE
AddComponent(/datum/component/butchering, 60, 80)
/obj/item/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
. = ..()
if(!proximity)
return
if(istype(target, /obj/structure/table))
var/obj/structure/table/T = target
T.deconstruct(FALSE)
else if(istype(target, /obj/machinery/computer))
var/obj/machinery/computer/C = target
C.attack_alien(user) //muh copypasta
else if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/A = target
if(!A.requiresID() || A.allowed(user)) //This is to prevent stupid shit like hitting a door with an arm blade, the door opening because you have acces and still getting a "the airlocks motors resist our efforts to force it" message.
return
if(A.locked)
to_chat(user, "<span class='warning'>The airlock's bolts prevent it from being forced!</span>")
return
if(A.hasPower())
user.visible_message("<span class='warning'>[user] jams [src] into the airlock and starts prying it open!</span>", "<span class='warning'>We start forcing [src] open.</span>", \
"<span class='italics'>You hear a metal screeching sound.</span>")
playsound(A, 'sound/machines/airlock_alien_prying.ogg', 100, 1)
if(!do_after(user, 100, target = A))
return
//user.say("Heeeeeeeeeerrre's Johnny!")
user.visible_message("<span class='warning'>[user] forces the airlock to open with [user.p_their()] [src]!</span>", "<span class='warning'>We force [src] to open.</span>", \
"<span class='italics'>You hear a metal screeching sound.</span>")
A.open(2)
/obj/item/melee/arm_blade/dropped(mob/user)
..()
if(can_drop)
new /obj/item/melee/synthetic_arm_blade(get_turf(user))
/***************************************\
|***********COMBAT TENTACLES*************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/tentacle
name = "Tentacle"
desc = "We ready a tentacle to grab items or victims with."
helptext = "We can use it once to retrieve a distant item. If used on living creatures, the effect depends on the intent: \
Help will simply drag them closer, Disarm will grab whatever they're holding instead of them, Grab will put the victim in our hold after catching it, \
and Harm will stun it, and stab it if we're also holding a sharp weapon. Cannot be used while in lesser form.\
This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10
dna_cost = 2
loudness = 2
req_human = 1
weapon_type = /obj/item/gun/magic/tentacle
weapon_name_simple = "tentacle"
silent = TRUE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_tentacle"
action_background_icon_state = "bg_ling"
/obj/item/gun/magic/tentacle
name = "tentacle"
desc = "A fleshy tentacle that can stretch out and grab things or people."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "tentacle"
item_state = "tentacle"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL | NOBLUDGEON
slot_flags = NONE
flags_1 = NONE
w_class = WEIGHT_CLASS_HUGE
ammo_type = /obj/item/ammo_casing/magic/tentacle
fire_sound = 'sound/effects/splat.ogg'
force = 0
max_charges = 1
fire_delay = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
/obj/item/gun/magic/tentacle/Initialize(mapload, silent)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
if(ismob(loc))
if(!silent)
loc.visible_message("<span class='warning'>[loc.name]\'s arm starts stretching inhumanly!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a tentacle.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
else
to_chat(loc, "<span class='notice'>You prepare to extend a tentacle.</span>")
/obj/item/gun/magic/tentacle/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "<span class='warning'>The [name] is not ready yet.</span>")
/obj/item/gun/magic/tentacle/process_chamber()
. = ..()
if(charges == 0)
qdel(src)
/obj/item/gun/magic/tentacle/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] coils [src] tightly around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (OXYLOSS)
/obj/item/ammo_casing/magic/tentacle
name = "tentacle"
desc = "A tentacle."
projectile_type = /obj/item/projectile/tentacle
caliber = "tentacle"
icon_state = "tentacle_end"
firing_effect_type = null
var/obj/item/gun/magic/tentacle/gun //the item that shot it
/obj/item/ammo_casing/magic/tentacle/Initialize()
gun = loc
. = ..()
/obj/item/ammo_casing/magic/tentacle/Destroy()
gun = null
return ..()
/obj/item/projectile/tentacle
name = "tentacle"
icon_state = "tentacle_end"
pass_flags = PASSTABLE
damage = 0
damage_type = BRUTE
range = 8
hitsound = 'sound/weapons/thudswoosh.ogg'
var/chain
var/obj/item/ammo_casing/magic/tentacle/source //the item that shot it
/obj/item/projectile/tentacle/Initialize()
source = loc
. = ..()
/obj/item/projectile/tentacle/fire(setAngle)
if(firer)
chain = firer.Beam(src, icon_state = "tentacle", time = INFINITY, maxdistance = INFINITY, beam_sleep_time = 1)
..()
/obj/item/projectile/tentacle/proc/reset_throw(mob/living/carbon/human/H)
if(H.in_throw_mode)
H.throw_mode_off() //Don't annoy the changeling if he doesn't catch the item
/obj/item/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
if(H.get_active_held_item() && !H.get_inactive_held_item())
H.swap_hand()
if(H.get_active_held_item())
return
C.grabbedby(H)
C.grippedby(H, instant = TRUE) //instant aggro grab
/obj/item/projectile/tentacle/proc/tentacle_stab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
for(var/obj/item/I in H.held_items)
if(I.get_sharpness())
C.visible_message("<span class='danger'>[H] impales [C] with [H.p_their()] [I.name]!</span>", "<span class='userdanger'>[H] impales you with [H.p_their()] [I.name]!</span>")
C.apply_damage(I.force, BRUTE, BODY_ZONE_CHEST)
H.do_item_attack_animation(C, used_item = I)
H.add_mob_blood(C)
playsound(get_turf(H),I.hitsound,75,1)
return
/obj/item/projectile/tentacle/on_hit(atom/target, blocked = FALSE)
var/mob/living/carbon/human/H = firer
if(blocked >= 100)
return 0
if(isitem(target))
var/obj/item/I = target
if(!I.anchored)
to_chat(firer, "<span class='notice'>You pull [I] right into your grasp.</span>")
H.put_in_hands(I) //Because throwing it is goofy as fuck and unreliable. If you land the tentacle despite the penalties to accuracy, you should have your reward.
. = 1
else if(isliving(target))
var/mob/living/L = target
if(!L.anchored && !L.throwing)//avoid double hits
if(iscarbon(L))
var/mob/living/carbon/C = L
var/firer_intent = INTENT_HARM
var/mob/M = firer
if(istype(M))
firer_intent = M.a_intent
switch(firer_intent)
if(INTENT_HELP)
C.visible_message("<span class='danger'>[L] is pulled by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2)
return 1
if(INTENT_DISARM)
var/obj/item/I = C.get_active_held_item()
if(I)
if(C.dropItemToGround(I))
C.visible_message("<span class='danger'>[I] is yanked off [C]'s hand by [src]!</span>","<span class='userdanger'>A tentacle pulls [I] away from you!</span>")
on_hit(I) //grab the item as if you had hit it directly with the tentacle
return 1
else
to_chat(firer, "<span class='danger'>You can't seem to pry [I] off [C]'s hands!</span>")
return 0
else
to_chat(firer, "<span class='danger'>[C] has nothing in hand to disarm!</span>")
return 0
if(INTENT_GRAB)
C.visible_message("<span class='danger'>[L] is grabbed by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2, H, TRUE, TRUE, callback=CALLBACK(src, .proc/tentacle_grab, H, C))
return 1
if(INTENT_HARM)
C.visible_message("<span class='danger'>[L] is thrown towards [H] by a tentacle!</span>","<span class='userdanger'>A tentacle grabs you and throws you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2, H, TRUE, TRUE, callback=CALLBACK(src, .proc/tentacle_stab, H, C))
return 1
else
L.visible_message("<span class='danger'>[L] is pulled by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
L.throw_at(get_step_towards(H,L), 8, 2)
. = 1
/obj/item/projectile/tentacle/Destroy()
qdel(chain)
source = null
return ..()
/***************************************\
|****************SHIELD*****************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/shield
name = "Organic Shield"
desc = "We reform one of our arms into a hard shield."
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 20
dna_cost = 1
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_shield"
action_background_icon_state = "bg_ling"
weapon_type = /obj/item/shield/changeling
weapon_name_simple = "shield"
/obj/effect/proc_holder/changeling/weapon/shield/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) //So we can read the absorbedcount.
if(!changeling)
return
var/obj/item/shield/changeling/S = ..(user)
S.remaining_uses = round(changeling.absorbedcount * 3)
return TRUE
/obj/item/shield/changeling
name = "shield-like mass"
desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield."
item_flags = ABSTRACT | DROPDEL
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "ling_shield"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
block_chance = 50
var/remaining_uses //Set by the changeling ability.
/obj/item/shield/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
if(ismob(loc))
loc.visible_message("<span class='warning'>The end of [loc.name]\'s hand inflates rapidly, forming a huge shield-like mass!</span>", "<span class='warning'>We inflate our hand into a strong shield.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/shield/changeling/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(src)
return 0
else
remaining_uses--
return ..()
/***************************************\
|*********SPACE SUIT + HELMET***********|
\***************************************/
/obj/effect/proc_holder/changeling/suit/organic_space_suit
name = "Organic Space Suit"
desc = "We grow an organic suit to protect ourselves from space exposure."
helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Cannot be used in lesser form. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 20
dna_cost = 2
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_space_suit"
action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
suit_name_simple = "flesh shell"
helmet_name_simple = "space helmet"
recharge_slowdown = 0.5
blood_on_castoff = 1
/obj/item/clothing/suit/space/changeling
name = "flesh mass"
icon_state = "lingspacesuit"
desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel."
item_flags = DROPDEL
clothing_flags = STOPSPRESSUREDAMAGE //Not THICKMATERIAL because it's organic tissue, so if somebody tries to inject something into it, it still ends up in your blood. (also balance but muh fluff)
allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/oxygen)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) //No armor at all.
/obj/item/clothing/suit/space/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh rapidly inflates, forming a bloated mass around [loc.p_their()] body!</span>", "<span class='warning'>We inflate our flesh, creating a spaceproof suit!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
START_PROCESSING(SSobj, src)
/obj/item/clothing/suit/space/changeling/process()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.reagents.add_reagent("salbutamol", REAGENTS_METABOLISM)
/obj/item/clothing/head/helmet/space/changeling
name = "flesh mass"
icon_state = "lingspacehelmet"
desc = "A covering of pressure and temperature-resistant organic tissue with a glass-like chitin front."
item_flags = DROPDEL
clothing_flags = STOPSPRESSUREDAMAGE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/obj/item/clothing/head/helmet/space/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/***************************************\
|*****************ARMOR*****************|
\***************************************/
/obj/effect/proc_holder/changeling/suit/armor
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
dna_cost = 1
loudness = 2
req_human = 1
recharge_slowdown = 0.25
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armor"
action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/armor/changeling
helmet_type = /obj/item/clothing/head/helmet/changeling
suit_name_simple = "armor"
helmet_name_simple = "helmet"
/obj/item/clothing/suit/armor/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin."
icon_state = "lingarmor"
item_flags = DROPDEL
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
flags_inv = HIDEJUMPSUIT
cold_protection = 0
heat_protection = 0
/obj/item/clothing/suit/armor/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh turns black, quickly transforming into a hard, chitinous mass!</span>", "<span class='warning'>We harden our flesh, creating a suit of armor!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/clothing/head/helmet/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin with transparent chitin in front."
icon_state = "lingarmorhelmet"
item_flags = DROPDEL
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
/obj/item/clothing/head/helmet/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
@@ -1,59 +0,0 @@
//Strained Muscles: Temporary speed boost at the cost of rapid damage
//Limited because of hardsuits and such; ideally, used for a quick getaway
/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."
chemical_cost = 15
dna_cost = 1
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
active = 0 //Whether or not you are a hedgehog
action_icon = 'icons/obj/implants.dmi'
action_icon_state = "adrenal"
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.5
else
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
changeling.chem_recharge_slowdown -= 0.5
if(stacks >= 20)
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
user.Knockdown(60)
user.emote("gasp")
INVOKE_ASYNC(src, .proc/muscle_loop, user)
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(MOVESPEED_ID_CHANGELING_MUSCLES, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
active = !active
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
user.Knockdown(40)
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
changeling.chem_recharge_slowdown -= 0.5
break
stacks++
//user.take_bodypart_damage(stacks * 0.03, 0)
user.adjustStaminaLoss(stacks*1.3) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
if(stacks == 10) //Warning message that the stacks are getting too high
to_chat(user, "<span class='warning'>Our legs are really starting to hurt...</span>")
sleep(40)
while(!active && stacks) //Damage stacks decrease fairly rapidly while not in sanic mode
stacks--
sleep(20)
@@ -1,383 +0,0 @@
//Sigils: Rune-like markings on the ground with various effects.
/obj/effect/clockwork/sigil
name = "sigil"
desc = "A strange set of markings drawn on the ground."
clockwork_desc = "A sigil of some purpose."
icon_state = "sigil"
layer = LOW_OBJ_LAYER
alpha = 50
resistance_flags = NONE
var/affects_servants = FALSE
var/stat_affected = CONSCIOUS
var/sigil_name = "Sigil"
var/resist_string = "glows blinding white" //string for when a null rod blocks its effects, "glows [resist_string]"
var/check_antimagic = TRUE
var/check_holy = FALSE
/obj/effect/clockwork/sigil/attackby(obj/item/I, mob/living/user, params)
if(I.force)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] scatters [src] with [I]!</span>", "<span class='danger'>You scatter [src] with [I]!</span>")
qdel(src)
return 1
return ..()
/obj/effect/clockwork/sigil/attack_tk(mob/user)
return //you can't tk stomp sigils, but you can hit them with something
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/clockwork/sigil/attack_hand(mob/user)
if(iscarbon(user) && !user.stat)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] stamps out [src]!</span>", "<span class='danger'>You stomp on [src], scattering it into thousands of particles.</span>")
qdel(src)
return TRUE
. = ..()
/obj/effect/clockwork/sigil/ex_act(severity)
visible_message("<span class='warning'>[src] scatters into thousands of particles.</span>")
qdel(src)
/obj/effect/clockwork/sigil/Crossed(atom/movable/AM)
..()
if(isliving(AM))
var/mob/living/L = AM
if(L.stat <= stat_affected)
if((!is_servant_of_ratvar(L) || (affects_servants && is_servant_of_ratvar(L))) && (L.mind || L.has_status_effect(STATUS_EFFECT_SIGILMARK)) && !isdrone(L))
var/atom/I = L.anti_magic_check(check_antimagic, check_holy)
if(I)
if(isitem(I))
L.visible_message("<span class='warning'>[L]'s [I.name] [resist_string], protecting [L.p_them()] from [src]'s effects!</span>", \
"<span class='userdanger'>Your [I.name] [resist_string], protecting you!</span>")
return
sigil_effects(L)
/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L)
//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar'Sian cultists are damaged and knocked down for a longer time
/obj/effect/clockwork/sigil/transgression
name = "dull sigil"
desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground."
icon = 'icons/effects/clockwork_effects.dmi'
clockwork_desc = "A sigil that will stun the next non-Servant to cross it."
icon_state = "sigildull"
layer = HIGH_SIGIL_LAYER
alpha = 75
color = "#FAE48C"
light_range = 1.4
light_power = 1
light_color = "#FAE48C"
sigil_name = "Sigil of Transgression"
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
var/target_flashed = L.flash_act()
for(var/mob/living/M in viewers(5, src))
if(!is_servant_of_ratvar(M) && M != L)
M.flash_act()
if(iscultist(L))
to_chat(L, "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>")
L.adjustBruteLoss(10)
L.Knockdown(80, FALSE)
L.visible_message("<span class='warning'>[src] appears around [L] in a burst of light!</span>", \
"<span class='userdanger'>[target_flashed ? "An unseen force":"The glowing sigil around you"] holds you in place!</span>")
L.Stun(40)
L.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/sigil/transgression(get_turf(src))
qdel(src)
//Sigil of Submission: After a short time, converts any non-servant standing on it. Knocks down and silences them for five seconds afterwards.
/obj/effect/clockwork/sigil/submission
name = "ominous sigil"
desc = "A luminous golden sigil. Something about it really bothers you."
clockwork_desc = "A sigil that will enslave any non-Servant that remains on it for 8 seconds. Cannot penetrate mindshield implants."
icon_state = "sigilsubmission"
layer = LOW_SIGIL_LAYER
alpha = 125
color = "#FAE48C"
light_range = 2 //soft light
light_power = 0.9
light_color = "#FAE48C"
stat_affected = UNCONSCIOUS
resist_string = "glows faintly yellow"
var/convert_time = 80
var/delete_on_finish = TRUE
sigil_name = "Sigil of Submission"
var/glow_type = /obj/effect/temp_visual/ratvar/sigil/submission
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
var/turf/T = get_turf(src)
var/has_sigil = FALSE
var/has_servant = FALSE
if(locate(/obj/effect/clockwork/sigil/transgression) in T)
has_sigil = TRUE
for(var/mob/living/M in range(3, src))
if(is_servant_of_ratvar(M) && !M.stat)
has_servant = TRUE
if(!has_sigil && !has_servant)
visible_message("<span class='danger'>[src] strains into a gentle violet color, but quietly fades...</span>")
return
L.visible_message("<span class='warning'>[src] begins to glow a piercing magenta!</span>", "<span class='sevtug'>You feel something start to invade your mind...</span>")
var/oldcolor = color
animate(src, color = "#AF0AAF", time = convert_time, flags = ANIMATION_END_NOW)
var/obj/effect/temp_visual/ratvar/sigil/glow
if(glow_type)
glow = new glow_type(get_turf(src))
animate(glow, alpha = 255, time = convert_time)
var/end_time = world.time+convert_time
while(world.time < end_time && get_turf(L) == get_turf(src))
stoplag(1)
if(get_turf(L) != get_turf(src))
if(glow)
qdel(glow)
animate(src, color = oldcolor, time = 20, flags = ANIMATION_END_NOW)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
return
if(is_eligible_servant(L))
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>")
if(add_servant_of_ratvar(L))
L.log_message("conversion was done with a [sigil_name]", LOG_ATTACK, color="BE8700")
if(iscarbon(L))
var/mob/living/carbon/M = L
M.uncuff()
var/brutedamage = L.getBruteLoss()
var/burndamage = L.getFireLoss()
if(brutedamage || burndamage)
L.adjustBruteLoss(-(brutedamage * 0.25))
L.adjustFireLoss(-(burndamage * 0.25))
L.Knockdown(50) //Completely defenseless for five seconds - mainly to give them time to read over the information they've just been presented with
if(iscarbon(L))
var/mob/living/carbon/C = L
C.silent += 5
var/message = "[sigil_name] in [get_area(src)] <span class='sevtug'>[is_servant_of_ratvar(L) ? "successfully converted" : "failed to convert"]</span>"
for(var/M in GLOB.mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, L)
to_chat(M, "[link] <span class='heavy_brass'>[message] [L.real_name]!</span>")
else if(is_servant_of_ratvar(M))
if(M == L)
to_chat(M, "<span class='heavy_brass'>[message] you!</span>")
else
to_chat(M, "<span class='heavy_brass'>[message] [L.real_name]!</span>")
animate(src, color = oldcolor, time = 20, flags = ANIMATION_END_NOW)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
//Sigil of Transmission: Serves as an access point for powered structures.
/obj/effect/clockwork/sigil/transmission
name = "suspicious sigil"
desc = "A glowing orange sigil. The air around it feels staticky."
clockwork_desc = "A sigil that serves as power generation and a battery for clockwork structures, linked to all other sigils of its type."
icon_state = "sigiltransmission"
alpha = 50
color = "#EC8A2D"
light_color = "#EC8A2D"
resist_string = "glows faintly"
sigil_name = "Sigil of Transmission"
affects_servants = TRUE
/obj/effect/clockwork/sigil/transmission/Initialize()
. = ..()
update_icon()
/obj/effect/clockwork/sigil/transmission/ex_act(severity)
if(severity == 3)
adjust_clockwork_power(500) //Light explosions charge the network!
visible_message("<span class='warning'>[src] flares a brilliant orange!</span>")
else
..()
/obj/effect/clockwork/sigil/transmission/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
var/structure_number = 0
for(var/obj/structure/destructible/clockwork/powered/P in range(SIGIL_ACCESS_RANGE, src))
structure_number++
to_chat(user, "<span class='[get_clockwork_power() ? "brass":"alloy"]'>It is storing <b>[DisplayPower(get_clockwork_power())]</b> of shared power, \
and <b>[structure_number]</b> clockwork structure[structure_number == 1 ? " is":"s are"] in range.</span>")
if(iscyborg(user))
to_chat(user, "<span class='brass'>You can recharge from the [sigil_name] by crossing it.</span>")
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
if(is_servant_of_ratvar(L))
if(iscyborg(L))
charge_cyborg(L)
else if(get_clockwork_power())
to_chat(L, "<span class='brass'>You feel a slight, static shock.</span>")
/obj/effect/clockwork/sigil/transmission/proc/charge_cyborg(mob/living/silicon/robot/cyborg)
if(!cyborg_checks(cyborg))
return
to_chat(cyborg, "<span class='brass'>You start to charge from the [sigil_name]...</span>")
if(!do_after(cyborg, 50, target = src, extra_checks = CALLBACK(src, .proc/cyborg_checks, cyborg, TRUE)))
return
var/giving_power = min(FLOOR(cyborg.cell.maxcharge - cyborg.cell.charge, MIN_CLOCKCULT_POWER), get_clockwork_power()) //give the borg either all our power or their missing power floored to MIN_CLOCKCULT_POWER
if(adjust_clockwork_power(-giving_power))
cyborg.visible_message("<span class='warning'>[cyborg] glows a brilliant orange!</span>")
var/previous_color = cyborg.color
cyborg.color = list("#EC8A2D", "#EC8A2D", "#EC8A2D", rgb(0,0,0))
cyborg.apply_status_effect(STATUS_EFFECT_POWERREGEN, giving_power * 0.1) //ten ticks, restoring 10% each
animate(cyborg, color = previous_color, time = 100)
addtimer(CALLBACK(cyborg, /atom/proc/update_atom_colour), 100)
/obj/effect/clockwork/sigil/transmission/proc/cyborg_checks(mob/living/silicon/robot/cyborg, silent)
if(!cyborg.cell)
if(!silent)
to_chat(cyborg, "<span class='warning'>You have no cell!</span>")
return FALSE
if(!get_clockwork_power())
if(!silent)
to_chat(cyborg, "<span class='warning'>There is no power available across sigils!</span>")
return FALSE
if(cyborg.cell.charge > cyborg.cell.maxcharge - MIN_CLOCKCULT_POWER)
if(!silent)
to_chat(cyborg, "<span class='warning'>You are already at maximum charge!</span>")
return FALSE
if(cyborg.has_status_effect(STATUS_EFFECT_POWERREGEN))
if(!silent)
to_chat(cyborg, "<span class='warning'>You are already regenerating power!</span>")
return FALSE
return TRUE
/obj/effect/clockwork/sigil/transmission/update_icon()
var/power_charge = get_clockwork_power()
if(GLOB.ratvar_awakens)
alpha = 255
else
alpha = min(CEILING(initial(alpha) + power_charge * 0.02, 35), 255)
var/r = alpha * 0.02
var/p = max(alpha * 0.01, 0.1)
if(!power_charge && light_range != 0)
set_light(0)
else if(r != light_range || p != light_power)
set_light(r, p)
//Vitality Matrix: Drains health from non-servants to heal or even revive servants.
/obj/effect/clockwork/sigil/vitality
name = "comforting sigil"
desc = "A faint blue sigil. Looking at it makes you feel protected."
clockwork_desc = "A sigil that will drain non-Servants that remain on it. Servants that remain on it will be healed if it has any vitality drained."
icon_state = "sigilvitality"
layer = SIGIL_LAYER
alpha = 125
color = "#123456"
affects_servants = TRUE
stat_affected = DEAD
resist_string = "glows shimmering yellow"
sigil_name = "Vitality Matrix"
var/revive_cost = 150
var/sigil_active = FALSE
var/animation_number = 3 //each cycle increments this by 1, at 4 it produces an animation and resets
var/static/list/damage_heal_order = list(CLONE, TOX, BURN, BRUTE, OXY) //we heal damage in this order
/obj/effect/clockwork/sigil/vitality/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='[GLOB.clockwork_vitality ? "inathneq_small":"alloy"]'>It has access to <b>[GLOB.ratvar_awakens ? "INFINITE":GLOB.clockwork_vitality]</b> units of vitality.</span>")
if(GLOB.ratvar_awakens)
to_chat(user, "<span class='inathneq_small'>It can revive Servants at no cost!</span>")
else
to_chat(user, "<span class='inathneq_small'>It can revive Servants at a cost of <b>[revive_cost]</b> vitality.</span>")
/obj/effect/clockwork/sigil/vitality/sigil_effects(mob/living/L)
if((is_servant_of_ratvar(L) && L.suiciding) || sigil_active)
return
animate(src, alpha = 255, time = 10, flags = ANIMATION_END_NOW) //we may have a previous animation going. finish it first, then do this one without delay.
sleep(10)
//as long as they're still on the sigil and are either not a servant or they're a servant AND it has remaining vitality
var/consumed_vitality
while(L && (!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && (GLOB.ratvar_awakens || GLOB.clockwork_vitality))) && get_turf(L) == get_turf(src) && !L.buckled)
sigil_active = TRUE
if(animation_number >= 4)
new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animation_number = 0
animation_number++
if(!is_servant_of_ratvar(L))
var/vitality_drained = 0
if(L.stat == DEAD && !consumed_vitality)
consumed_vitality = TRUE //Prevent the target from being consumed multiple times
vitality_drained = L.maxHealth
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/wandodeath.ogg', 50, 1)
L.visible_message("<span class='warning'>[L] collapses in on [L.p_them()]self as [src] flares bright blue!</span>")
to_chat(L, "<span class='inathneq_large'>\"[text2ratvar("Your life will not be wasted.")]\"</span>")
for(var/obj/item/W in L)
if(!L.dropItemToGround(W))
qdel(W)
L.dust()
else
if(!GLOB.ratvar_awakens && L.stat == CONSCIOUS)
vitality_drained = L.adjustToxLoss(1)
else
vitality_drained = L.adjustToxLoss(1.5)
if(vitality_drained)
GLOB.clockwork_vitality += vitality_drained
else
break
else
if(L.stat == DEAD)
var/revival_cost = revive_cost
if(GLOB.ratvar_awakens || L.suiciding) // No cost if Ratvar is summoned or if you're reviving a convert who suicided
revival_cost = 0
var/mob/dead/observer/ghost = L.get_ghost(TRUE)
if(GLOB.clockwork_vitality >= revival_cost && (ghost || (L.mind && L.mind.active)))
if(L.has_status_effect(STATUS_EFFECT_ICHORIAL_STAIN))
visible_message("<span class='boldwarning'>[src] strains, but nothing happens...</span>")
if(L.pulledby)
to_chat(L.pulledby, "<span class='userdanger'>[L] was already revived recently by a vitality matrix! Wait a bit longer!</span>")
break
else
if(ghost)
ghost.reenter_corpse()
L.revive(1, 1)
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/staff_healing.ogg', 50, 1)
to_chat(L, "<span class='inathneq'>\"[text2ratvar("You will be okay, child.")]\"</span>")
L.apply_status_effect(STATUS_EFFECT_ICHORIAL_STAIN)
GLOB.clockwork_vitality -= revival_cost
break
if(!L.client || L.client.is_afk())
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [L.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, null, ROLE_SERVANT_OF_RATVAR, 50, L)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(L, "<span class='userdanger'>Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form!</span>")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(L)]) to replace an inactive clock cultist.")
L.ghostize(0)
C.transfer_ckey(L, FALSE)
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/staff_healing.ogg', 50, 1)
L.visible_message("<span class='warning'>[L]'s eyes suddenly open wide, gleaming with renewed vigor for the cause!</span>", "<span class='inathneq'>\"[text2ratvar("Awaken!")]\"</span>")
break
var/vitality_for_cycle = 3
if(!GLOB.ratvar_awakens)
if(L.stat == CONSCIOUS)
vitality_for_cycle = 2
vitality_for_cycle = min(GLOB.clockwork_vitality, vitality_for_cycle)
var/vitality_used = L.heal_ordered_damage(vitality_for_cycle, damage_heal_order)
if(!vitality_used)
break
if(!GLOB.ratvar_awakens)
if(GLOB.clockwork_vitality <= 0)
break
GLOB.clockwork_vitality -= vitality_used
sleep(2)
if(sigil_active)
animation_number = initial(animation_number)
sigil_active = FALSE
animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW)
@@ -1,76 +0,0 @@
//Ratvarian spear: A relatively fragile spear from the Celestial Derelict. Deals extreme damage to silicons and enemy cultists, but doesn't last long when summoned.
/obj/item/clockwork/weapon/ratvarian_spear
name = "ratvarian spear"
desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy."
clockwork_desc = "A powerful spear of Ratvarian making. It's more effective against enemy cultists and silicons."
icon_state = "ratvarian_spear"
item_state = "ratvarian_spear"
force = 15 //Extra damage is dealt to targets in attack()
throwforce = 25
armour_penetration = 10
sharpness = IS_SHARP_ACCURATE
attack_verb = list("stabbed", "poked", "slashed")
hitsound = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
var/bonus_burn = 5
/obj/item/clockwork/weapon/ratvarian_spear/ratvar_act()
if(GLOB.ratvar_awakens) //If Ratvar is alive, the spear is extremely powerful
force = 20
bonus_burn = 10
throwforce = 40
armour_penetration = 50
else
force = initial(force)
bonus_burn = initial(bonus_burn)
throwforce = initial(throwforce)
armour_penetration = initial(armour_penetration)
/obj/item/clockwork/weapon/ratvarian_spear/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='inathneq_small'>Attacks on living non-Servants will generate <b>[bonus_burn]</b> units of vitality.</span>")
if(!iscyborg(user))
to_chat(user, "<span class='brass'>Throwing the spear will do massive damage, break the spear, and knock down the target.</span>")
/obj/item/clockwork/weapon/ratvarian_spear/attack(mob/living/target, mob/living/carbon/human/user)
. = ..()
if(!QDELETED(target) && target.stat != DEAD && !target.anti_magic_check(chargecost = 0) && !is_servant_of_ratvar(target)) //we do bonus damage on attacks unless they're a servant, have a null rod, or are dead
var/bonus_damage = bonus_burn //normally a total of 20 damage, 30 with ratvar
if(issilicon(target))
target.visible_message("<span class='warning'>[target] shudders violently at [src]'s touch!</span>", "<span class='userdanger'>ERROR: Temperature rising!</span>")
bonus_damage *= 5 //total 40 damage on borgs, 70 with ratvar
else if(iscultist(target) || isconstruct(target))
to_chat(target, "<span class='userdanger'>Your body flares with agony at [src]'s presence!</span>")
bonus_damage *= 3 //total 30 damage on cultists, 50 with ratvar
GLOB.clockwork_vitality += target.adjustFireLoss(bonus_damage) //adds the damage done to existing vitality
/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/target)
var/turf/T = get_turf(target)
if(isliving(target))
var/mob/living/L = target
if(is_servant_of_ratvar(L))
if(L.put_in_active_hand(src))
L.visible_message("<span class='warning'>[L] catches [src] out of the air!</span>")
else
L.visible_message("<span class='warning'>[src] bounces off of [L], as if repelled by an unseen force!</span>")
else if(!..())
if(!L.anti_magic_check())
if(issilicon(L) || iscultist(L))
L.Knockdown(100)
else
L.Knockdown(40)
GLOB.clockwork_vitality += L.adjustFireLoss(bonus_burn * 3) //normally a total of 40 damage, 70 with ratvar
break_spear(T)
else
..()
/obj/item/clockwork/weapon/ratvarian_spear/proc/break_spear(turf/T)
if(src)
if(!T)
T = get_turf(src)
if(T) //make sure we're not in null or something
T.visible_message("<span class='warning'>[src] [pick("cracks in two and fades away", "snaps in two and dematerializes")]!</span>")
new /obj/effect/temp_visual/ratvar/spearbreak(T)
action.weapon_reset(RATVARIAN_SPEAR_COOLDOWN)
@@ -1,524 +0,0 @@
/obj/item/clockwork/slab //Clockwork slab: The most important tool in Ratvar's arsenal. Allows scripture recital, tutorials, and generates components.
name = "clockwork slab"
desc = "A strange metal tablet. A clock in the center turns around and around."
clockwork_desc = "A link between you and the Celestial Derelict. It contains information, recites scripture, and is your most vital tool as a Servant.<br>\
It can be used to link traps and triggers by attacking them with the slab. Keep in mind that traps linked with one another will activate in tandem!"
icon_state = "dread_ipad"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
var/inhand_overlay //If applicable, this overlay will be applied to the slab's inhand
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
var/busy //If the slab is currently being used by something
var/no_cost = FALSE //If the slab is admin-only and needs no components and has no scripture locks
var/speed_multiplier = 1 //multiples how fast this slab recites scripture
var/selected_scripture = SCRIPTURE_DRIVER
var/obj/effect/proc_holder/slab/slab_ability //the slab's current bound ability, for certain scripture
var/recollecting = FALSE //if we're looking at fancy recollection
var/recollection_category = "Default"
var/list/quickbound = list(/datum/clockwork_scripture/abscond, \
/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
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
name = "scripture motor"
quickbound = list()
no_cost = TRUE
/obj/item/clockwork/slab/debug
speed_multiplier = 0
no_cost = TRUE
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clockwork/slab/debug/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
/obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
/datum/clockwork_scripture/create_object/stargazer)
maximum_quickbound = 6 //we usually have one or two unique scriptures, so if ratvar is up let us bind one more
actions_types = list()
/obj/item/clockwork/slab/cyborg/engineer //three scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission, /datum/clockwork_scripture/create_object/stargazer)
/obj/item/clockwork/slab/cyborg/medical //five scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/ranged_ability/sentinels_compromise, \
/datum/clockwork_scripture/create_object/vitality_matrix)
/obj/item/clockwork/slab/cyborg/security //twoscriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/hateful_manacles, /datum/clockwork_scripture/ranged_ability/judicial_marker)
/obj/item/clockwork/slab/cyborg/peacekeeper //two scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/hateful_manacles, /datum/clockwork_scripture/ranged_ability/judicial_marker)
/obj/item/clockwork/slab/cyborg/janitor //six scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/stargazer, /datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor)
/obj/item/clockwork/slab/cyborg/service //six scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant,/datum/clockwork_scripture/create_object/stargazer, \
/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/clockwork_obelisk)
/obj/item/clockwork/slab/cyborg/miner //two scriptures, plus a spear and xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/spatial_gateway)
/obj/item/clockwork/slab/cyborg/access_display(mob/living/user)
if(!GLOB.ratvar_awakens)
to_chat(user, "<span class='warning'>Use the action buttons to recite your limited set of scripture!</span>")
else
..()
/obj/item/clockwork/slab/cyborg/ratvar_act()
..()
if(!GLOB.ratvar_awakens)
SStgui.close_uis(src)
/obj/item/clockwork/slab/Initialize()
. = ..()
update_slab_info(src)
START_PROCESSING(SSobj, src)
if(GLOB.ratvar_approaches)
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
/obj/item/clockwork/slab/Destroy()
STOP_PROCESSING(SSobj, src)
if(slab_ability && slab_ability.ranged_ability_user)
slab_ability.remove_ranged_ability()
slab_ability = null
return ..()
/obj/item/clockwork/slab/dropped(mob/user)
. = ..()
addtimer(CALLBACK(src, .proc/check_on_mob, user), 1) //dropped is called before the item is out of the slot, so we need to check slightly later
/obj/item/clockwork/slab/worn_overlays(isinhands = FALSE, icon_file)
. = list()
if(isinhands && item_state && inhand_overlay)
var/mutable_appearance/M = mutable_appearance(icon_file, "slab_[inhand_overlay]")
. += M
/obj/item/clockwork/slab/proc/check_on_mob(mob/user)
if(user && !(src in user.held_items) && slab_ability && slab_ability.ranged_ability_user) //if we happen to check and we AREN'T in user's hands, remove whatever ability we have
slab_ability.remove_ranged_ability()
//Power generation
/obj/item/clockwork/slab/process()
if(GLOB.ratvar_approaches && speed_multiplier == initial(speed_multiplier))
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
adjust_clockwork_power(0.1) //Slabs serve as very weak power generators on their own (no, not enough to justify spamming them)
/obj/item/clockwork/slab/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
to_chat(user, "<b>Quickbind</b> button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>.")
to_chat(user, "<b>Available power:</b> <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>")
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/clock/quickbind))
var/datum/action/item_action/clock/quickbind/Q = action
recite_scripture(quickbound[Q.scripture_index], user, FALSE)
//Scripture Recital
/obj/item/clockwork/slab/attack_self(mob/living/user)
if(iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"You reek of blood. You've got a lot of nerve to even look at that slab.\"</span>")
user.visible_message("<span class='warning'>A sizzling sound comes from [user]'s hands!</span>", "<span class='userdanger'>[src] suddenly grows extremely hot in your hands!</span>")
playsound(get_turf(user), 'sound/weapons/sear.ogg', 50, 1)
user.dropItemToGround(src)
user.emote("scream")
user.apply_damage(5, BURN, BODY_ZONE_L_ARM)
user.apply_damage(5, BURN, BODY_ZONE_R_ARM)
return 0
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='warning'>The information on [src]'s display shifts rapidly. After a moment, your head begins to pound, and you tear your eyes away.</span>")
user.confused += 5
user.dizziness += 5
return 0
if(busy)
to_chat(user, "<span class='warning'>[src] refuses to work, displaying the message: \"[busy]!\"</span>")
return 0
if(!no_cost && !can_recite_scripture(user))
to_chat(user, "<span class='nezbere'>[src] hums fitfully in your hands, but doesn't seem to do anything...</span>")
return 0
access_display(user)
/obj/item/clockwork/slab/AltClick(mob/living/user)
if(is_servant_of_ratvar(user) && linking && user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
linking = null
to_chat(user, "<span class='notice'>Object link canceled.</span>")
/obj/item/clockwork/slab/proc/access_display(mob/living/user)
if(!is_servant_of_ratvar(user))
return FALSE
ui_interact(user)
return TRUE
/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "clockwork_slab", name, 800, 420, master_ui, state)
ui.set_autoupdate(FALSE) //we'll update this occasionally, but not as often as possible
ui.set_style("clockwork")
ui.open()
/obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user)
if(!scripture || !user || !user.canUseTopic(src) || (!no_cost && !can_recite_scripture(user)))
return FALSE
if(user.get_active_held_item() != src)
to_chat(user, "<span class='warning'>You need to hold the slab in your active hand to recite scripture!</span>")
return FALSE
var/initial_tier = initial(scripture.tier)
if(initial_tier != SCRIPTURE_PERIPHERAL)
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
to_chat(user, "<span class='warning'>That scripture is not unlocked, and cannot be recited!</span>")
return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
scripture_to_recite.slab = src
scripture_to_recite.invoker = user
scripture_to_recite.run_scripture()
return TRUE
//Guide to Serving Ratvar
/obj/item/clockwork/slab/proc/recollection()
var/list/textlist = list("If you're seeing this, file a bug report.")
if(GLOB.ratvar_awakens)
textlist = list("<font color=#BE8700 size=3><b>")
for(var/i in 1 to 100)
textlist += "HONOR RATVAR "
textlist += "</b></font>"
else
textlist = list("<font color=#BE8700 size=3><b><center>[text2ratvar("Purge all untruths and honor Engine.")]</center></b></font><br>\
\
<b><i>NOTICE:</b> This information is out of date. Read the Ark & You primer in your backpack or read the wiki page for current info.</i><br>\
<hr><br>\
These pages serve as the archives of Ratvar, the Clockwork Justiciar. This section of your slab has information on being as a Servant, advice for what to do next, and \
pointers for serving the master well. You should recommended that you check this area for help if you get stuck or need guidance on what to do next.<br><br>\
\
<i>Disclaimer: Many objects, terms, and phrases, such as Servant, Cache, and Slab, are capitalized like proper nouns. This is a quirk of the Ratvarian language; \
do not let it confuse you! You are free to use the names in pronoun form when speaking in normal languages.<br>")
return textlist.Join()
//Gets text for a certain section. "Default" is used for when you first open Recollection.
//Current sections (make sure to update this if you add one:
//- Basics
//- Terminology
//- Components
//- Scripture
//- Power
//- Conversion
/obj/item/clockwork/slab/proc/get_recollection_text(section)
var/list/dat = list()
switch(section)
if("Default")
dat += "You can browse the above sections as you please. They're designed to be read in order, but feel free to pick and choose between them."
if("Getting Started")
dat += "<font color=#BE8700 size=3>Getting Started</font><br><br>"
dat += "Welcome, Servant! This section houses the utmost basics of being a Servant of Ratvar, and is much more informal than the other sections. Being a Servant of \
Ratvar is a very complex role, with many systems, objects, and resources to use effectively and creatively.<br><br>"
dat += "This section of your clockwork slab covers everything that Servants have to be aware of, but is a long read because of how in-depth the systems are. Knowing \
how to use the tools at your disposal makes all the difference between a clueless Servant and a great one.<br><br>"
dat += "If this is your first time being a Servant, relax. It's very much possible that you'll fail, but it's impossible to learn without making mistakes. For the time \
being, use the Hierophant Network button in the top left-hand corner of your screen to try and get in touch with your fellow Servants; ignore the others for now. This button \
will let you send messages across space and time to all other Servants. This makes it great for coordinating, and you should use it often! <i>Note:</i> Using \
this will cause you to whisper your message aloud, so doing so in a public place is very suspicious and you should try to restrict it to private use.<br><br>"
dat += "If you aren't willing or don't have the time to read through every section, you can still help your teammates! Ask if they've set up a base. If they have, head there \
and ask however you can help; chances are there's always something. If not, it's your job as a Servant to get one up and running! Try to find a secluded, low-traffic area, \
like the auxiliary base or somewhere deep in maintenance. You'll want to go into the Drivers section of the slab and look for <i>Tinkerer's Cache.</i> Find a nice spot and \
create one. This serves as a storage for <i>components,</i> the cult's primary resource. (Your slab's probably produced a few by now.) By attacking that cache with this \
slab, you'll offload all your components into it, and all Servants will be able to use those components from any distance - all Tinkerer's Caches are linked!<br><br>"
dat += "Once you have a base up and running, contact your fellows and let them know. You should come back here often to drop off the slab's components, and your fellows \
should do the same, either in this cache or in ones of their own.<br><br>"
dat += "If you think you're confident in taking further steps to help the cult, feel free to move onto the other sections. If not, let your allies know that you're new and \
would appreciate the help they might offer you. Most experienced Servants would be happy to help; if everyone is inexperienced, then you'll have to step out of your comfort \
zone and read onto the other sections. It's very likely that you might fail, but don't worry too much about it; you can't learn effectively without making mistakes.<br><br>"
dat += "For now, welcome! If you're looking to learn, you should start with the <b>Basics</b> section, then move onto <b>Components</b> and <b>Scripture</b>. At the very \
least, you should read the <b><i>Conversion</i></b> section, as it outlines the most important aspects of being a Servant. Good luck!<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Basics")
dat += "<font color=#BE8700 size=3>Servant Basics</font><br><br>"
dat += "The first thing any Servant should know is their slab, inside and out. The clockwork slab is by far your most important tool. It allows you to speak with your \
fellow Servants, create components that fuel many of your abilities, use those abilities, and should be kept safe and hidden on your person at all times. If you have not \
done so already, it's a good idea to check for any fellow Servants using the Hierophant Network button in the top-left corner of your screen; due to the cult's nature, \
teamwork is an instrumental component of your success.<br><br>" //get it? component? ha!
dat += "As a Servant of Ratvar, the tools you are given focus around building and maintaining bases and outposts. A great deal of your power comes from stationary \
structures, and without constructing a base somewhere, it's essentially impossible to succeed. Finding a good spot to build a base can be difficult, and it's recommended \
that you choose an area in low-traffic part of the station (such as the auxiliary base). Make sure to disconnect any cameras in the area beforehand.<br><br>"
dat += "Because of how complex being a Servant is, it isn't possible to fit much information into this section. It's highly recommended that you read the <b>Components</b> \
and <b>Scripture</b> sections next. Not knowing how these two systems work will cripple both you and your fellows, and lead to a frustrating experience for everyone.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Terminology")
dat += "<font color=#BE8700 size=3>Common Servant Terminology</font><br>"
dat += "<i>This isn't intended to be read all at once; you are advised to treat it moreso as a glossary.</i><br><br>"
dat += "<font color=#BE8700 size=3>General</font><br>"
dat += "<font color=#BE8700><b>Servant:</b></font> A person or robot who serves Ratvar. You are one of these.<br>"
dat += "<font color=#BE8700><b>Cache:</b></font> A <i>Tinkerer's Cache</i>, which is a structure that stores and creates components.<br>"
dat += "<font color=#BE8700><b>CV:</b></font> Construction Value. All clockwork structures, floors, and walls increase this number.<br>"
dat += "<font color=#BE8700><b>Vitality:</b></font> Used for healing effects, produced by Ratvarian spear attacks and Vitality Matrices.<br>"
dat += "<font color=#BE8700><b>Geis:</b></font> An important scripture used to make normal crew and robots into Servants of Ratvar.<br>"
dat += "<font color=#BE8700><b>Ark:</b></font> The cult's win condition, a huge structure that needs to be defended.<br><br>"
dat += "<font color=#BE8700 size=3>Items</font><br>"
dat += "<font color=#BE8700><b>Slab:</b></font> A clockwork slab, a Servant's most important tool. You're holding one! Keep it safe and hidden.<br>"
dat += "<font color=#BE8700><b>Visor:</b></font> A judicial visor, which is a pair of glasses that can smite an area for a brief stun and delayed explosion.<br>"
dat += "<font color=#BE8700><b>Wraith Specs:</b></font> Wraith spectacles, which provide true sight (X-ray, night vision) but damage the wearer's eyes.<br>"
dat += "<font color=#BE8700><b>Spear:</b></font> A Ratvarian spear, which is a very powerful melee weapon that produces Vitality.<br>"
dat += "<font color=#BE8700><b>Fabricator:</b></font> A replica fabricator, which converts objects into clockwork versions.<br><br>"
dat += "<font color=#BE8700 size=3>Constructs</font><br>"
dat += "<font color=#BE8700><b>Marauder:</b></font> A clockwork marauder, which is a powerful bodyguard that hides in its owner.<br><br>"
dat += "<font color=#BE8700 size=3>Structures (* = requires power)</font><br>"
dat += "<font color=#BE8700><b>Warden:</b></font> An ocular warden, which is a ranged turret that damages non-Servants that see it.<br>"
dat += "<font color=#BE8700><b>Prism*:</b></font> A prolonging prism, which delays the shuttle for two minutes at a huge power cost.<br><br>"
dat += "<font color=#BE8700><b>Motor*:</b></font> A mania motor, which serves as area-denial through negative effects and eventual conversion.<br>"
dat += "<font color=#BE8700><b>Daemon*:</b></font> A tinkerer's daemon, which quickly creates components.<br>"
dat += "<font color=#BE8700><b>Obelisk*:</b></font> A clockwork obelisk, which can broadcast large messages and allows limited teleportation.<br>"
dat += "<font color=#BE8700 size=3>Sigils</font><br>"
dat += "<i>Note: Sigils can be stacked on top of one another, making certain sigils very effective when paired!</i><br>"
dat += "<font color=#BE8700><b>Transgression:</b></font> Stuns the first non-Servant to cross it for ten seconds and blinds others nearby. Disappears on use.<br>"
dat += "<font color=#BE8700><b>Submission:</b></font> Converts the first non-Servant to stand on the sigil for seven seconds. Disappears on use.<br>"
dat += "<font color=#BE8700><b>Matrix:</b></font> Drains health from non-Servants, producing Vitality. Can heal and revive Servants.<br>"
dat += "<font color=#BE8700><b>Accession:</b></font> Identical to the Sigil of Submission, but doesn't disappear on use. It can also convert a single mindshielded target, but will disappear after doing this.<br>"
dat += "<font color=#BE8700><b>Transmission:</b></font> Drains and stores power for clockwork structures. Feeding it brass sheets will create additional power.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Components")
dat += "<font color=#BE8700 size=3>Components & Their Uses</font><br><br>"
dat += "<b>Components</b> are your primary resource as a Servant. There are five types of component, with each one being used in different roles:<br><br>"
dat += "Although this is a good rule of thumb, their effects become much more nuanced when used together. For instance, a turret might have both belligerent eyes and \
vanguard cogwheels as construction requirements, because it defends its allies by harming its enemies.<br><br>"
dat += "Components' primary use is fueling <b>scripture</b> (covered in its own section), and they can be created through various ways. This clockwork slab, for instance, \
will make a random component of every type - or a specific one, if you choose a target component from the interface - every <b>remove me already</b>. This number will increase \
as the amount of Servants in the covenant increase; additionally, slabs can only produce components when held by a Servant, and holding more than one slab will cause both \
of them to halt progress until one of them is removed from their person.<br><br>"
dat += "Your slab has an internal storage of components, but it isn't meant to be the main one. Instead, there's a <b>global storage</b> of components that can be \
added to through various ways. Anything that needs components will first draw them from the global storage before attempting to draw them from the slab. Most methods of \
component production add to the global storage. You can also offload components from your slab into the global storage by using it on a Tinkerer's Cache, a structure whose \
primary purpose is to do just that (although it will also slowly produce components when placed near a brass wall.)<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Scripture")
dat += "<font color=#BE8700 size=3>The Ancient Scripture</font><br><br>"
dat += "If you have experience with the Nar'Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \
of the cult's supernatural ability comes from. The Servant equivalent of runes is called <b>scripture</b>, and unlike runes, scripture is loaded into your clockwork slab.<br><br>"
dat += "Each piece of scripture has widely-varying effects. Your most important scripture, <i>Geis</i>, is obvious and suspicious, but charges your slab with energy and allows \
you to attack a non-Servant in melee range to restrain them and begin converting them into a Servant. This is just one example; each piece of scripture can be simple or \
complex, be obvious or have hidden mechanics that can only be found through trial and error.<br><br>"
dat += "Any given piece of scripture has a component cost listed in its \"Recite\" button. The acronyms for the components should be obvious if you've read about components \
already; reciting this piece of scripture will consume the listed components, first from the global storage and then from your slab. Note that failing to recite a piece of \
scripture will <i>not</i> consume the components required to recite it.<br><br>"
dat += "It should also be noted that some scripture cannot be recited alone. Especially with more powerful scripture, you may need multiple Servants to recite a piece of \
scripture; both of you will need to stand still until the recital completes. <i>Only human and silicon Servants are valid for scripture recital!</i> Constructs cannot help \
in reciting scripture.<br><br>"
dat += "Finally, scripture is separated into three \"tiers\" based on power: Drivers, Scripts, and Applications.[prob(1) ? " (The Revenant tier was removed a long time ago. \
Get with the times.)" : ""] You can view the requirements to unlock each tier in its scripture list. Once a tier is unlocked, it's unlocked permanently; the cult only needs to fill the \
requirement for unlocking a tier once!<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Power")
dat += "<font color=#BE8700 size=3>Power! Unlimited Power!</font><br><br>"
dat += "In the early stages of the cult, the only resource that must be actively worried about is components. However, as new scripture is unlocked, a new resource \
becomes necessary: <b>power</b>. Almost all clockwork structures require power to function in some way. There is nothing special about this power; it's mere electricity, \
and can be harnessed in several ways.<br><br>"
dat += "To begin with, if there is no other source of power nearby, structures will draw from the area's APC, assuming it has one. This is inefficient and ill-advised as \
anything but a last resort. Instead, it is recommended that a <b>Sigil of Transmission</b> is created. This sigil serves as both battery and power generator for nearby clockwork \
structures, and those structures will happily draw power from the sigil before they resort to APCs.<br><br>"
dat += "Generating power is less easy. The most reliable and efficient way is using brass sheets; attacking a sigil of transmission with brass sheets will convert them \
to power, at a rate of <b>[DisplayPower(POWER_FLOOR)]</b> per sheet. (Brass sheets are created from replica fabricators, which are explained more in detail in the <b>Conversion</b> section.) \
Activating a sigil of transmission will also cause it to drain power from the nearby area, which, while effective, serves as an obvious tell that there is something wrong.<br><br>"
dat += "Without power, many structures will not function, making a base vulnerable to attack. For this reason, it is critical that you keep an eye on your power reserves and \
ensure that they remain comfortably high.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Conversion")
dat += "<font color=#BE8700 size=3>Growing the Ranks</font><br><br>"
dat += "Because the Servants of Ratvar are a cult, the main method to gain more power is to \"enlighten\" normal crew into new Servants. When a crewmember is converted, \
they become a full-fledged Servant, ready and willing to serve the cause of Ratvar. It should also be noted that <i>silicon crew, such as cyborgs and the AI, can be \
converted just like normal crew</i> and will gain special abilities; this is covered later. This section will also cover converting the station's structure itself; walls, \
floors, windows, tables, and other objects can all be converted into clockwork versions, and serve an important purpose.<br><br>"
dat += "<font color=#BE8700><b>A Note on Geis:</b></font> There are several ways to convert humans and silicons. However, the most important tool to making them work is \
<b>Geis</b>, a Driver-tier scripture. Using it whispers an invocation very quickly and charges your slab with power. In addition to <i>making the slab visible in your hand,</i> \
you can now use it on a target within melee range to bind and mute them. It is by far your most reliable tool for capturing potential converts and targets, though it is incredibly \
obvious. In addition, you are unable to take any actions other than moving while your target is bound. The binding will last for 25 seconds and mute for about 13 seconds, though \
allies can use Geis to refresh these effects.<br><br>"
dat += "<font color=#BE8700><b>Converting:</b></font> The two methods of conversion are the <b>sigil of submission</b>, whose purpose is to do so, and the <b>mania motor.</b> \
The sigil of submission is a sigil that, when stood on by a non-Servant for eight seconds, will convert that non-Servant. This is the only practical way to convert targets. \
Sigils of submission are cheap, early, and permanent! Make sure sigils of submission are placed only in bases or otherwise hidden spots, or with a sigil of transgression on them. \
The mania motor, however, is generally unreliable and unlocked later, only converting those who stand near it for an extended period.<br><br>"
dat += "<font color=#BE8700><b>Converting Humans:</b></font> For obvious reasons, humans are the most common conversion target. Because every crew member is different, and \
may be armed with different equipment, you should take precautions to ensure that they aren't able to resist. If able, removing a headset is essential, as is restraining \
them through handcuffs, cable ties, or other restraints. Some crew, like security, are also implanted with mindshield implants; these will prevent conversion and must be \
surgically removed before they are an eligible convert. <i>Note:</i> The captain is <i>never</i> an eligible convert and should instead be killed or imprisoned. If security \
begins administering mindshield implants, this will greatly inhibit conversion. Also note that mindshield implants can be broken by a sigil of accession automatically, but \
the sigil will disappear.<br><br>"
dat += "<font color=#BE8700><b>Converting Silicons:</b></font> Due to their robotic nature, silicons are generally more predictable than humans in terms of conversion. \
However, they are also much, much harder to subdue, especially cyborgs. The easiest way to convert a cyborg is by using Geis to restrain them, then dragging them to a sigil \
of submission. If you stack a sigil of transgression and a sigil of submission, a crossing cyborg will be stunned and helpless to escape before they are converted.<br><br>"
dat += "Converting AIs is very often the hardest task of the cult, and has been the downfall of countless successful Servants. Their omnipresence across the station, \
coupled with their secure location and ability to lock themselves securely, makes them a powerful target. However, once the AI itself is reached, it is usually completely \
helpless to resist its own conversion. A very common tactic is to take advantage of a converted cyborg to rush the AI before it is able to react.<br><br>"
dat += "Even once an AI is converted, care must be taken to ensure that it remains hidden. Not only does the AI's core become brassy and thus obvious to an outside \
observer, but <i>the AI loses the ability to speak in anything but Ratvarian.</i> For this reason, it has to remain completely silent over common radio channels if stealth \
is at all a priority. This is suspicious and will rapidly lead to the crew checking on it, which usually results in the cult's outing. It is, however, necessary to convert \
all AIs present on the station before the Ark becomes invokable, so this must be done at some point.<br><br>"
dat += "<font color=#BE8700><b>Converting the Station:</b></font> Converted objects all serve a purpose and are important to the cult's success. To convert objects, \
a Servant needs to use a <b>replica fabricator,</b> a handheld tool that uses power to replace objects with clockwork versions. Different clockwork objects have different \
effects and are often crucial. The most noteworthy are <b>clockwork walls,</b> which automatically \"link\" to any nearby Tinkerer's Caches, causing them to <b>slowly \
generate components.</b> This is incredibly useful for obvious reasons, and creating a clockwork wall near every Tinkerer's Cache should be prioritized. Clockwork floors \
will slowly heal any toxin damage suffered by Servants standing on them, and clockwork airlocks can only be opened by Servants.<br><br>"
dat += "The replica fabricator itself is also worth noting. In addition to replacing objects, it can also create brass sheets at the cost of power by using the \
fabricator in-hand. It can also be used to repair any damaged clockwork structures.<br><br>"
dat += "Replacing objects is almost as, if not as important as, converting new Servants. A base is impossible to manage without clockwork walls at the very least, and \
once the cult has been outed and the crew are actively searching, there is little reason not to use as many as possible.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
else
dat += "<font color=#BE8700 size=3>404: [section ? section : "Section"] Not Found!</font><br><br>\
One of the cogscarabs must've misplaced this section, because the game wasn't able to find any info regarding it. Report this to the coders!"
return "<br><br>[dat.Join()]<br><br>"
//Gets the quickbound scripture as a text block.
/obj/item/clockwork/slab/proc/get_recollection_quickbinds()
var/list/dat = list()
dat += "<font color=#BE8700 size=3>Quickbound Scripture</font><br>\
<i>You can have up to five scriptures bound to action buttons for easy use.</i><br><br>"
if(LAZYLEN(quickbound))
for(var/i in 1 to maximum_quickbound)
if(LAZYLEN(quickbound) < i || !quickbound[i])
dat += "A <b>Quickbind</b> slot, currently set to <b><font color=#BE8700>Nothing</font></b>.<br>"
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
dat += "A <b>Quickbind</b> slot, currently set to <b><font color=[get_component_color_bright(initial(quickbind_slot.primary_component))]>[initial(quickbind_slot.name)]</font></b>.<br>"
return dat.Join()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
var/list/data = list()
data["power"] = "<b><font color=#B18B25>[DisplayPower(get_clockwork_power())]</b> power is available for scripture and other consumers.</font>"
switch(selected_scripture) //display info based on selected scripture tier
if(SCRIPTURE_DRIVER)
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permanently unlocked.</b></font>"
if(SCRIPTURE_SCRIPT)
if(SSticker.scripture_states[SCRIPTURE_SCRIPT])
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permanently unlocked.</b></font>"
else
data["tier_info"] = "<font color=#B18B25><i>These scriptures will automatically unlock when the Ark is halfway ready or if [DisplayPower(SCRIPT_UNLOCK_THRESHOLD)] of power is reached.</i></font>"
if(SCRIPTURE_APPLICATION)
if(SSticker.scripture_states[SCRIPTURE_APPLICATION])
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permanently unlocked.</b></font>"
else
data["tier_info"] = "<font color=#B18B25><i>Unlock these optional scriptures by converting another servant or if [DisplayPower(APPLICATION_UNLOCK_THRESHOLD)] of power is reached..</i></font>"
data["selected"] = selected_scripture
data["scripturecolors"] = "<font color=#DAAA18>Scriptures in <b>yellow</b> are related to construction and building.</font><br>\
<font color=#6E001A>Scriptures in <b>red</b> are related to attacking and offense.</font><br>\
<font color=#1E8CE1>Scriptures in <b>blue</b> are related to healing and defense.</font><br>\
<font color=#AF0AAF>Scriptures in <b>purple</b> are niche but still important!</font><br>\
<font color=#DAAA18><i>Scriptures with italicized names are important to success.</i></font>"
generate_all_scripture()
data["scripture"] = list()
for(var/s in GLOB.all_scripture)
var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
if(S.tier == selected_scripture) //display only scriptures of the selected tier
var/scripture_color = get_component_color_bright(S.primary_component)
var/list/temp_info = list("name" = "<font color=[scripture_color]><b>[S.name]</b></font>",
"descname" = "<font color=[scripture_color]>([S.descname])</font>",
"tip" = "[S.desc]\n[S.usage_tip]",
"required" = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])",
"type" = "[S.type]",
"quickbind" = S.quickbind)
if(S.important)
temp_info["name"] = "<i>[temp_info["name"]]</i>"
var/found = quickbound.Find(S.type)
if(found)
temp_info["bound"] = "<b>[found]</b>"
if(S.invokers_required > 1)
temp_info["invokers"] = "<font color=#B18B25>Invokers: <b>[S.invokers_required]</b></font>"
data["scripture"] += list(temp_info)
data["recollection"] = recollecting
if(recollecting)
data["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(\
list("name" = "Getting Started", "desc" = "First-time servant? Read this first."), \
list("name" = "Basics", "desc" = "A primer on how to play as a servant."), \
list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."), \
list("name" = "Components", "desc" = "Information on components, your primary resource."), \
list("name" = "Scripture", "desc" = "Information on scripture, ancient tools used by the cult."), \
list("name" = "Power", "desc" = "The power system that certain objects use to function."), \
list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause."), \
)
data["rec_text"] = recollection()
data["rec_section"] = GLOB.ratvar_awakens ? "" : get_recollection_text(recollection_category)
data["rec_binds"] = GLOB.ratvar_awakens ? "" : get_recollection_quickbinds()
return data
/obj/item/clockwork/slab/ui_act(action, params)
switch(action)
if("toggle")
recollecting = !recollecting
if("recite")
INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["category"]), usr, FALSE)
if("select")
selected_scripture = params["category"]
if("bind")
var/datum/clockwork_scripture/path = text2path(params["category"]) //we need a path and not a string
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
quickbound -= path
else
quickbound[found_index] = null //otherwise, leave it as a null so the scripture maintains position
update_quickbind()
else
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
if(LAZYLEN(quickbound) >= target_index)
S = quickbound[target_index]
if(S != path)
quickbind_to_slot(path, target_index)
if("rec_category")
recollection_category = params["category"]
ui_interact(usr)
return 1
/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))
return
while(LAZYLEN(quickbound) < index)
quickbound += null
var/datum/clockwork_scripture/quickbind_slot = GLOB.all_scripture[quickbound[index]]
if(quickbind_slot && !quickbind_slot.quickbind)
return //we can't unbind things we can't normally bind
quickbound[index] = scripture
update_quickbind()
/obj/item/clockwork/slab/proc/update_quickbind()
for(var/datum/action/item_action/clock/quickbind/Q in actions)
qdel(Q) //regenerate all our quickbound scriptures
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/action/item_action/clock/quickbind/Q = new /datum/action/item_action/clock/quickbind(src)
Q.scripture_index = i
var/datum/clockwork_scripture/quickbind_slot = GLOB.all_scripture[quickbound[i]]
Q.name = "[quickbind_slot.name] ([Q.scripture_index])"
Q.desc = quickbind_slot.quickbind_desc
Q.button_icon_state = quickbind_slot.name
Q.UpdateButtonIcon()
if(isliving(loc))
Q.Grant(loc)
@@ -1,121 +0,0 @@
//Construct shells that can be activated by ghosts.
/obj/item/clockwork/construct_chassis
name = "construct chassis"
desc = "A shell formed out of brass, presumably for housing machinery."
clockwork_desc = "A construct chassis. It can be activated at any time by a willing ghost."
var/construct_name = "basic construct"
var/construct_desc = "<span class='alloy'>There is no construct for this chassis. Report this to a coder.</span>"
icon_state = "anime_fragment"
resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_HUGE
var/creation_message = "<span class='brass'>The chassis shudders and hums to life!</span>"
var/construct_type //The construct this shell will create
/obj/item/clockwork/construct_chassis/Initialize()
. = ..()
var/area/A = get_area(src)
if(A && construct_type)
notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_CONSTRUCT, ignore_dnr_observers = TRUE)
GLOB.poi_list += src
LAZYADD(GLOB.mob_spawners[name], src)
/obj/item/clockwork/construct_chassis/Destroy()
GLOB.poi_list -= src
LAZYREMOVE(GLOB.mob_spawners[name], src)
if(!LAZYLEN(GLOB.mob_spawners[name]))
GLOB.mob_spawners -= name
. = ..()
/obj/item/clockwork/construct_chassis/examine(mob/user)
clockwork_desc = "[clockwork_desc]<br>[construct_desc]"
..()
clockwork_desc = initial(clockwork_desc)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user)
if(w_class >= WEIGHT_CLASS_HUGE)
to_chat(user, "<span class='warning'>[src] is too cumbersome to carry! Drag it around instead!</span>")
return
. = ..()
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/item/clockwork/construct_chassis/attack_ghost(mob/dead/observer/user)
if(!user.can_reenter_round())
return FALSE
if(!SSticker.mode)
to_chat(user, "<span class='danger'>You cannot use that before the game has started.</span>")
return
if(QDELETED(src))
to_chat(user, "<span class='danger'>You were too late! Better luck next time.</span>")
return
user.forceMove(get_turf(src)) //If we attack through the alert, jump to the chassis so we know what we're getting into
if(alert(user, "Become a [construct_name]? You can no longer be cloned!", construct_name, "Yes", "Cancel") == "Cancel")
return
if(QDELETED(src))
to_chat(user, "<span class='danger'>You were too late! Better luck next time.</span>")
return
pre_spawn()
visible_message(creation_message)
var/mob/living/construct = new construct_type(get_turf(src))
user.transfer_ckey(construct, FALSE)
post_spawn(construct)
qdel(user)
qdel(src)
/obj/item/clockwork/construct_chassis/proc/pre_spawn() //Some things might change before the construct spawns; override those on a subtype basis in this proc
return
/obj/item/clockwork/construct_chassis/proc/post_spawn(mob/living/construct) //And some things might change after it
return
//Marauder armor, used to create clockwork marauders - sturdy frontline combatants that can deflect projectiles.
/obj/item/clockwork/construct_chassis/clockwork_marauder
name = "marauder armor"
desc = "A pile of sleek and well-polished brass armor. A small red gemstone sits in its faceplate."
icon_state = "marauder_armor"
construct_name = "clockwork marauder"
construct_desc = "<span class='neovgre_small'>It will become a <b>clockwork marauder,</b> a well-rounded frontline combatant.</span>"
creation_message = "<span class='neovgre_small bold'>Crimson fire begins to rage in the armor as it rises into the air with its armaments!</span>"
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
//Cogscarab shell, used to create cogcarabs - fragile but zippy little drones that build and maintain the base.
/obj/item/clockwork/construct_chassis/cogscarab
name = "cogscarab shell"
desc = "A small, complex shell that resembles a repair drone, but much larger and made out of brass."
icon_state = "cogscarab_shell"
construct_name = "cogscarab"
construct_desc = "<span class='alloy'>It will become a <b>cogscarab,</b> a small and fragile drone that builds, repairs, and maintains.</span>"
creation_message = "<span class='alloy bold'>The cogscarab clicks and whirrs as it hops up and springs to life!</span>"
construct_type = /mob/living/simple_animal/drone/cogscarab
w_class = WEIGHT_CLASS_SMALL
var/infinite_resources = TRUE
var/static/obj/item/seasonal_hat //Share it with all other scarabs, since we're from the same cult!
/obj/item/clockwork/construct_chassis/cogscarab/Initialize()
. = ..()
if(GLOB.servants_active)
infinite_resources = FALSE //For any that are somehow spawned in late
/obj/item/clockwork/construct_chassis/cogscarab/pre_spawn()
if(infinite_resources)
//During rounds where they can't interact with the station, let them experiment with builds
construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
if(!seasonal_hat)
var/obj/item/drone_shell/D = locate() in GLOB.poi_list
if(D && D.possible_seasonal_hats.len)
seasonal_hat = pick(D.possible_seasonal_hats)
else
seasonal_hat = "none"
/obj/item/clockwork/construct_chassis/cogscarab/post_spawn(mob/living/construct)
if(infinite_resources) //Allow them to build stuff and recite scripture
var/list/cached_stuff = construct.GetAllContents()
for(var/obj/item/clockwork/replica_fabricator/F in cached_stuff)
F.uses_power = FALSE
for(var/obj/item/clockwork/slab/S in cached_stuff)
S.no_cost = TRUE
if(seasonal_hat && seasonal_hat != "none")
var/obj/item/hat = new seasonal_hat(construct)
construct.equip_to_slot_or_del(hat, SLOT_HEAD)
@@ -1,218 +0,0 @@
//Judicial visor: Grants the ability to smite an area and knocking down the unfaithful nearby every thirty seconds.
/obj/item/clothing/glasses/judicial_visor
name = "judicial visor"
desc = "A strange purple-lensed visor. Looking at it inspires an odd sense of guilt."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "judicial_visor_0"
item_state = "sunglasses"
resistance_flags = FIRE_PROOF | ACID_PROOF
flash_protect = 1
var/active = FALSE //If the visor is online
var/recharging = FALSE //If the visor is currently recharging
var/obj/effect/proc_holder/judicial_visor/blaster
var/recharge_cooldown = 300 //divided by 10 if ratvar is alive
actions_types = list(/datum/action/item_action/clock/toggle_visor)
/obj/item/clothing/glasses/judicial_visor/Initialize()
. = ..()
GLOB.all_clockwork_objects += src
blaster = new(src)
blaster.visor = src
/obj/item/clothing/glasses/judicial_visor/Destroy()
GLOB.all_clockwork_objects -= src
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
blaster.visor = null
qdel(blaster)
return ..()
/obj/item/clothing/glasses/judicial_visor/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot != SLOT_GLASSES)
return 0
return ..()
/obj/item/clothing/glasses/judicial_visor/equipped(mob/living/user, slot)
..()
if(slot != SLOT_GLASSES)
update_status(FALSE)
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
return 0
if(is_servant_of_ratvar(user))
update_status(TRUE)
else
update_status(FALSE)
if(iscultist(user)) //Cultists spontaneously combust
to_chat(user, "<span class='heavy_brass'>\"Consider yourself judged, whelp.\"</span>")
to_chat(user, "<span class='userdanger'>You suddenly catch fire!</span>")
user.adjust_fire_stacks(5)
user.IgniteMob()
return 1
/obj/item/clothing/glasses/judicial_visor/dropped(mob/user)
. = ..()
addtimer(CALLBACK(src, .proc/check_on_mob, user), 1) //dropped is called before the item is out of the slot, so we need to check slightly later
/obj/item/clothing/glasses/judicial_visor/proc/check_on_mob(mob/user)
if(user && src != user.get_item_by_slot(SLOT_GLASSES)) //if we happen to check and we AREN'T in the slot, we need to remove our shit from whoever we got dropped from
update_status(FALSE)
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
/obj/item/clothing/glasses/judicial_visor/attack_self(mob/user)
if(is_servant_of_ratvar(user) && src == user.get_item_by_slot(SLOT_GLASSES))
blaster.toggle(user)
/obj/item/clothing/glasses/judicial_visor/proc/update_status(change_to)
if(recharging || !isliving(loc))
icon_state = "judicial_visor_0"
return 0
if(active == change_to)
return 0
var/mob/living/L = loc
active = change_to
icon_state = "judicial_visor_[active]"
L.update_action_buttons_icon()
L.update_inv_glasses()
if(!is_servant_of_ratvar(L) || L.stat)
return 0
switch(active)
if(TRUE)
to_chat(L, "<span class='notice'>As you put on [src], its lens begins to glow, information flashing before your eyes.</span>\n\
<span class='heavy_brass'>Judicial visor active. Use the action button to gain the ability to smite the unworthy.</span>")
if(FALSE)
to_chat(L, "<span class='notice'>As you take off [src], its lens darkens once more.</span>")
return 1
/obj/item/clothing/glasses/judicial_visor/proc/recharge_visor(mob/living/user)
if(!src)
return 0
recharging = FALSE
if(user && src == user.get_item_by_slot(SLOT_GLASSES))
to_chat(user, "<span class='brass'>Your [name] hums. It is ready.</span>")
else
active = FALSE
icon_state = "judicial_visor_[active]"
if(user)
user.update_action_buttons_icon()
user.update_inv_glasses()
/obj/effect/proc_holder/judicial_visor
active = FALSE
ranged_mousepointer = 'icons/effects/visor_reticule.dmi'
var/obj/item/clothing/glasses/judicial_visor/visor
/obj/effect/proc_holder/judicial_visor/proc/toggle(mob/user)
var/message
if(active)
message = "<span class='brass'>You dispel the power of [visor].</span>"
remove_ranged_ability(message)
else
message = "<span class='brass'><i>You harness [visor]'s power.</i> <b>Left-click to place a judicial marker!</b></span>"
add_ranged_ability(user, message)
/obj/effect/proc_holder/judicial_visor/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return
if(ranged_ability_user.incapacitated() || !visor || visor != ranged_ability_user.get_item_by_slot(SLOT_GLASSES))
remove_ranged_ability()
return
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return FALSE
if(target in view(7, get_turf(ranged_ability_user)))
visor.recharging = TRUE
visor.update_status()
for(var/obj/item/clothing/glasses/judicial_visor/V in ranged_ability_user.GetAllContents())
if(V == visor)
continue
V.recharging = TRUE //To prevent exploiting multiple visors to bypass the cooldown
V.update_status()
addtimer(CALLBACK(V, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), (GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown) * 2)
clockwork_say(ranged_ability_user, text2ratvar("Kneel, heathens!"))
ranged_ability_user.visible_message("<span class='warning'>[ranged_ability_user]'s judicial visor fires a stream of energy at [target], creating a strange mark!</span>", "<span class='heavy_brass'>You direct [visor]'s power to [target]. You must wait for some time before doing this again.</span>")
var/turf/targetturf = get_turf(target)
new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user)
log_combat(ranged_ability_user, targetturf, "created a judicial marker")
ranged_ability_user.update_action_buttons_icon()
ranged_ability_user.update_inv_glasses()
addtimer(CALLBACK(visor, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown)//Cooldown is reduced by 10x if Ratvar is up
remove_ranged_ability()
return TRUE
return FALSE
//Judicial marker: Created by the judicial visor. Immediately applies Belligerent and briefly knocks down, then after 3 seconds does large damage and briefly knocks down again
/obj/effect/clockwork/judicial_marker
name = "judicial marker"
desc = "You get the feeling that you shouldn't be standing here."
clockwork_desc = "A sigil that will soon erupt and smite any unenlightened nearby."
icon = 'icons/effects/96x96.dmi'
icon_state = ""
pixel_x = -32
pixel_y = -32
layer = BELOW_MOB_LAYER
var/mob/user
/obj/effect/clockwork/judicial_marker/Initialize(mapload, caster)
. = ..()
set_light(1.4, 2, "#FE9C11")
user = caster
INVOKE_ASYNC(src, .proc/judicialblast)
/obj/effect/clockwork/judicial_marker/singularity_act()
return
/obj/effect/clockwork/judicial_marker/singularity_pull()
return
/obj/effect/clockwork/judicial_marker/proc/judicialblast()
playsound(src, 'sound/magic/magic_missile.ogg', 50, 1, 1, 1)
flick("judicial_marker", src)
for(var/mob/living/carbon/C in range(1, src))
var/datum/status_effect/belligerent/B = C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
if(!QDELETED(B))
B.duration = world.time + 30
C.Knockdown(5) //knocks down for half a second if affected
sleep(!GLOB.ratvar_approaches ? 16 : 10)
name = "judicial blast"
layer = ABOVE_ALL_MOB_LAYER
flick("judicial_explosion", src)
set_light(1.4, 2, "#B451A1")
sleep(13)
name = "judicial explosion"
var/targetsjudged = 0
playsound(src, 'sound/effects/explosion_distant.ogg', 100, 1, 1, 1)
set_light(0)
for(var/mob/living/L in range(1, src))
if(is_servant_of_ratvar(L))
continue
var/atom/I = L.anti_magic_check()
if(I)
if(isitem(I))
L.visible_message("<span class='warning'>Strange energy flows into [L]'s [I.name]!</span>", \
"<span class='userdanger'>Your [I.name] shields you from [src]!</span>")
continue
L.Knockdown(15) //knocks down briefly when exploding
if(!iscultist(L))
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='userdanger'>[!issilicon(L) ? "An unseen force slams you into the ground!" : "ERROR: Motor servos disabled by external source!"]</span>")
else
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='heavy_brass'>\"Keep an eye out, filth.\"</span>\n<span class='userdanger'>A burst of heat crushes you against the ground!</span>")
L.adjust_fire_stacks(2) //sets cultist targets on fire
L.IgniteMob()
L.adjustFireLoss(5)
targetsjudged++
if(!QDELETED(L))
L.adjustBruteLoss(20) //does a decent amount of damage
log_combat(user, L, "struck with a judicial blast")
to_chat(user, "<span class='brass'><b>[targetsjudged ? "Successfully judged <span class='neovgre'>[targetsjudged]</span>":"Judged no"] heretic[targetsjudged == 1 ? "":"s"].</b></span>")
sleep(3) //so the animation completes properly
qdel(src)
/obj/effect/clockwork/judicial_marker/ex_act(severity)
return
@@ -1,151 +0,0 @@
//////////////////
// APPLICATIONS //
//////////////////
//Sigil of Transmission: Creates a sigil of transmission that can drain and store power for clockwork structures.
/datum/clockwork_scripture/create_object/sigil_of_transmission
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!")
channel_time = 70
power_cost = 200
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
creator_message = "<span class='brass'>A sigil silently appears below you. It will automatically power clockwork structures near it and will drain power when activated.</span>"
usage_tip = "Cyborgs can charge from this sigil by remaining over it for 5 seconds."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
//Mania Motor: Creates a malevolent transmitter that will broadcast the whispers of Sevtug into the minds of nearby nonservants, causing a variety of mental effects at a power cost.
/datum/clockwork_scripture/create_object/mania_motor
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!")
channel_time = 80
power_cost = 750
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
creator_message = "<span class='brass'>You form a mania motor, which causes minor damage and negative mental effects in non-Servants.</span>"
observer_message = "<span class='warning'>A two-pronged machine rises from the ground!</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "It will also cure hallucinations and brain damage in nearby Servants."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
//Clockwork Obelisk: Creates a powerful obelisk that can be used to broadcast messages or open a gateway to any servant or clockwork obelisk at a power cost.
/datum/clockwork_scripture/create_object/clockwork_obelisk
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!")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
creator_message = "<span class='brass'>You form a clockwork obelisk which can broadcast messages or produce Spatial Gateways.</span>"
observer_message = "<span class='warning'>A brass obelisk appears hanging in midair!</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "Producing a gateway has a high power cost. Gateways to or between clockwork obelisks receive double duration and uses."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Clockwork Obelisk, which can send messages or open Spatial Gateways with power."
//Clockwork Marauder: Creates a construct shell for a clockwork marauder, a well-rounded frontline fighter.
/datum/clockwork_scripture/create_object/construct/clockwork_marauder
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.")
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>"
usage_tip = "Reciting this scripture multiple times in a short period will cause it to take longer!"
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a clockwork marauder, used for frontline combat."
object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
combat_construct = TRUE
var/static/recent_marauders = 0
var/static/time_since_last_marauder = 0
var/static/scaled_recital_time = 0
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/update_construct_limit()
var/human_servants = 0
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
var/mob/living/L = M.current
if(ishuman(L) && L.stat != DEAD)
human_servants++
construct_limit = round(CLAMP((human_servants / 4), 1, 3)) - recent_marauders //1 per 4 human servants, maximum of 3, reduced by recent marauder creation
if(recent_marauders)
to_chat(invoker, "<span class='warning'>The Hierophant Network is depleted by a summoning in the last [DisplayTimeText(MARAUDER_SCRIPTURE_SCALING_THRESHOLD, TRUE)] - limiting the number of available marauders by [recent_marauders]!</span>")
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/pre_recital()
channel_time = initial(channel_time)
if(recent_marauders)
scaled_recital_time = min(recent_marauders * MARAUDER_SCRIPTURE_SCALING_TIME, MARAUDER_SCRIPTURE_SCALING_MAX)
to_chat(invoker, "<span class='warning'>The Hierophant Network is under strain from repeated summoning, making this scripture [DisplayTimeText(scaled_recital_time)] slower!</span>")
channel_time += scaled_recital_time
return TRUE
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/scripture_effects()
. = ..()
recent_marauders++
addtimer(CALLBACK(GLOBAL_PROC, .proc/marauder_reset),MARAUDER_SCRIPTURE_SCALING_THRESHOLD)
/proc/marauder_reset()
var/datum/clockwork_scripture/create_object/construct/clockwork_marauder/CM = new()
CM.recent_marauders--
qdel(CM)
//Summon Neovgre: Summon a very powerful combat mech that explodes when destroyed for massive damage.
/datum/clockwork_scripture/create_object/summon_arbiter
descname = "Powerful Assault Mech"
name = "Summon Neovgre, the Anima Bulwark"
desc = "Calls forth the mighty Anima Bulwark, a weapon of unmatched power,\
mech with superior defensive and offensive capabilities. It will \
steadily regenerate HP and triple its regeneration speed while standing \
on a clockwork tile. It will automatically draw power from nearby sigils of \
transmission should the need arise. Its Arbiter laser cannon can decimate foes \
from a range and is capable of smashing through any barrier presented to it. \
Be warned, choosing to pilot Neovgre is a lifetime commitment, once you are \
in you cannot leave and when it is destroyed it will explode catastrophically with you inside."
invocations = list("By the strength of the alloy...!!", "...call forth the Arbiter!!")
channel_time = 200 // This is a strong fucking weapon, 20 seconds channel time is getting off light I tell ya.
power_cost = 75000 //75 KW
usage_tip = "Neovgre is a powerful mech that will crush your enemies!"
invokers_required = 5
multiple_invokers_used = TRUE
object_path = /obj/mecha/combat/neovgre
tier = SCRIPTURE_APPLICATION
primary_component = BELLIGERENT_EYE
sort_priority = 2
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()
if(GLOB.neovgre_exists)
to_chat(invoker, "<span class='brass'>\"You've already got one...\"</span>")
return FALSE
return ..()
@@ -1,294 +0,0 @@
/////////////
// DRIVERS //
/////////////
//Stargazer: Creates a stargazer, a cheap power generator that utilizes starlight.
/datum/clockwork_scripture/create_object/stargazer
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!")
channel_time = 50
power_cost = 200
object_path = /obj/structure/destructible/clockwork/stargazer
creator_message = "<span class='brass'>You form a stargazer, which will generate power near starlight.</span>"
observer_message = "<span class='warning'>A large lantern-shaped machine forms!</span>"
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
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a stargazer, which generates power when near starlight."
/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)
to_chat(invoker, "<span class='danger'>Stargazers can't be built off-station.</span>")
return
return ..()
//Integration Cog: Creates an integration cog that can be inserted into APCs to passively siphon power.
/datum/clockwork_scripture/create_object/integration_cog
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!")
channel_time = 10
power_cost = 10
whispered = TRUE
object_path = /obj/item/clockwork/integration_cog
creator_message = "<span class='brass'>You form an integration cog, which can be inserted into an open APC to passively siphon power.</span>"
usage_tip = "Tampering isn't visible unless the APC is opened. You can use the cog on a locked APC to unlock it."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates an integration cog, which can be used to siphon power from an open APC."
//Sigil of Transgression: Creates a sigil of transgression, which briefly stuns and applies Belligerent to the first non-servant to cross it.
/datum/clockwork_scripture/create_object/sigil_of_transgression
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!")
channel_time = 50
power_cost = 50
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transgression
creator_message = "<span class='brass'>A sigil silently appears below you. The next non-Servant to cross it will be smitten.</span>"
usage_tip = "The sigil does not silence its victim, and is generally used to soften potential converts or would-be invaders."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transgression, which will briefly stun and slow the next non-Servant to cross it."
//Sigil of Submission: Creates a sigil of submission, which converts one heretic above it after a delay.
/datum/clockwork_scripture/create_object/sigil_of_submission
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!")
channel_time = 60
power_cost = 125
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/submission
creator_message = "<span class='brass'>A luminous sigil appears below you. Any non-Servants to cross it will be converted and healed of some of their wounds after 8 seconds if they do not move.</span>"
usage_tip = "This is the primary conversion method, though it will not penetrate mindshield implants."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Submission, which will convert non-Servants that remain on it."
//Kindle: Charges the slab with blazing energy. It can be released to stun and silence a target.
/datum/clockwork_scripture/ranged_ability/kindle
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!")
whispered = TRUE
channel_time = 20 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
power_cost = 125
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 5
slab_overlay = "volt"
ranged_type = /obj/effect/proc_holder/slab/kindle
ranged_message = "<span class='brass'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to stun!\n\
Click your slab to cancel.</b></span>"
timeout_time = 150
important = TRUE
quickbind = TRUE
quickbind_desc = "Stuns and mutes a target from a short range."
//Hateful Manacles: Applies restraints from melee over several seconds. The restraints function like handcuffs and break on removal.
/datum/clockwork_scripture/ranged_ability/hateful_manacles
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!")
channel_time = 15
power_cost = 25
whispered = TRUE
usage_tip = "The manacles are about as strong as zipties, and break when removed."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 6
ranged_type = /obj/effect/proc_holder/slab/hateful_manacles
slab_overlay = "hateful_manacles"
ranged_message = "<span class='neovgre_small'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to shackle!\n\
Click your slab to cancel.</b></span>"
timeout_time = 200
important = TRUE
quickbind = TRUE
quickbind_desc = "Applies handcuffs to a struck target."
//Vanguard: Provides twenty seconds of greatly increased stamina and stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed are applied to the invoker.
/datum/clockwork_scripture/vanguard
descname = "Self Stun Immunity"
name = "Vanguard"
desc = "Provides twenty seconds of greatly increased stamina and stun immunity. At the end of the twenty seconds, the invoker is knocked down for the equivalent of 25% of all stuns they absorbed. \
Excessive absorption will cause unconsciousness."
invocations = list("Shield me...", "...from darkness!")
channel_time = 30
power_cost = 75
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Allows you to temporarily have quickly regenerating stamina and absorb stuns. All stuns absorbed will affect you when disabled."
/datum/clockwork_scripture/vanguard/check_special_requirements()
if(!GLOB.ratvar_awakens && islist(invoker.stun_absorption) && invoker.stun_absorption["vanguard"] && invoker.stun_absorption["vanguard"]["end_time"] > world.time)
to_chat(invoker, "<span class='warning'>You are already shielded by a Vanguard!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/vanguard/scripture_effects()
if(GLOB.ratvar_awakens)
for(var/mob/living/L in view(7, get_turf(invoker)))
if(L.stat != DEAD && is_servant_of_ratvar(L))
L.apply_status_effect(STATUS_EFFECT_VANGUARD)
CHECK_TICK
else
invoker.apply_status_effect(STATUS_EFFECT_VANGUARD)
return TRUE
//Sentinel's Compromise: Allows the invoker to select a nearby servant and convert their brute, burn, and oxygen damage into half as much toxin damage.
/datum/clockwork_scripture/ranged_ability/sentinels_compromise
descname = "Convert Brute/Burn/Oxygen to Half Toxin"
name = "Sentinel's Compromise"
desc = "Charges your slab with healing power, allowing you to convert all of a target Servant's brute, burn, and oxygen damage to half as much toxin damage."
invocations = list("Mend the wounds of...", "...my inferior flesh.")
channel_time = 30
power_cost = 100
usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
quickbind = TRUE
quickbind_desc = "Allows you to convert a Servant's brute, burn, and oxygen damage to half toxin damage.<br><b>Click your slab to disable.</b>"
slab_overlay = "compromise"
ranged_type = /obj/effect/proc_holder/slab/compromise
ranged_message = "<span class='inathneq_small'><i>You charge the clockwork slab with healing power.</i>\n\
<b>Left-click a fellow Servant or yourself to heal!\n\
Click your slab to cancel.</b></span>"
//Abscond: Used to return to Reebe.
/datum/clockwork_scripture/abscond
descname = "Return to Reebe"
name = "Abscond"
desc = "Yanks you through space, returning you to home base."
invocations = list("As we bid farewell, and return to the stars...", "...we shall find our way home.")
whispered = TRUE
channel_time = 50
power_cost = 5
special_power_text = "POWERCOST to bring pulled creature"
special_power_cost = ABSCOND_ABDUCTION_COST
usage_tip = "This can't be used while on Reebe, for obvious reasons."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
important = TRUE
quickbind = TRUE
quickbind_desc = "Returns you to Reebe."
var/client_color
/datum/clockwork_scripture/abscond/check_special_requirements()
if(is_reebe(invoker.z))
to_chat(invoker, "<span class='danger'>You're already at Reebe.</span>")
return
if(!isturf(invoker.loc))
to_chat(invoker, "<span class='danger'>You must be visible to return!</span>")
return
return TRUE
/datum/clockwork_scripture/abscond/recital()
client_color = invoker.client.color
animate(invoker.client, color = "#AF0AAF", time = 50)
. = ..()
/datum/clockwork_scripture/abscond/scripture_effects()
var/mob/living/pulled_mob = (invoker.pulling && isliving(invoker.pulling) && get_clockwork_power(ABSCOND_ABDUCTION_COST)) ? invoker.pulling : null
var/turf/T
if(GLOB.ark_of_the_clockwork_justiciar)
T = get_step(GLOB.ark_of_the_clockwork_justiciar, SOUTH)
else
T = get_turf(pick(GLOB.servant_spawns))
if(!do_teleport(invoker, T, channel = TELEPORT_CHANNEL_CULT, forced = TRUE))
return
invoker.visible_message("<span class='warning'>[invoker] flickers and phases out of existence!</span>", \
"<span class='bold sevtug_small'>You feel a dizzying sense of vertigo as you're yanked back to Reebe!</span>")
T.visible_message("<span class='warning'>[invoker] flickers and phases into existence!</span>")
playsound(invoker, 'sound/magic/magic_missile.ogg', 50, TRUE)
playsound(T, 'sound/magic/magic_missile.ogg', 50, TRUE)
do_sparks(5, TRUE, invoker)
do_sparks(5, TRUE, T)
if(pulled_mob && do_teleport(pulled_mob, T, channel = TELEPORT_CHANNEL_CULT, forced = TRUE))
adjust_clockwork_power(-special_power_cost)
invoker.start_pulling(pulled_mob) //forcemove resets pulls, so we need to re-pull
if(invoker.client)
animate(invoker.client, color = client_color, time = 25)
/datum/clockwork_scripture/abscond/scripture_fail()
if(invoker && invoker.client)
animate(invoker.client, color = client_color, time = 10)
//Replicant: Creates a new clockwork slab.
/datum/clockwork_scripture/create_object/replicant
descname = "New Clockwork Slab"
name = "Replicant"
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
channel_time = 10
power_cost = 25
whispered = TRUE
object_path = /obj/item/clockwork/slab
creator_message = "<span class='brass'>You copy a piece of replicant alloy and command it into a new slab.</span>"
usage_tip = "This is inefficient as a way to produce components, as the slab produced must be held by someone with no other slabs to produce components."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 10
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a new Clockwork Slab."
//Wraith Spectacles: Creates a pair of wraith spectacles, which grant xray vision but damage vision slowly.
/datum/clockwork_scripture/create_object/wraith_spectacles
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!")
channel_time = 10
power_cost = 50
whispered = TRUE
object_path = /obj/item/clothing/glasses/wraith_spectacles
creator_message = "<span class='brass'>You form a pair of wraith spectacles, which grant true sight but cause gradual vision loss.</span>"
usage_tip = "\"True sight\" means that you are able to see through walls and in darkness."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
@@ -1,95 +0,0 @@
//No, not that kind.
/obj/structure/destructible/clockwork/trap
name = "base clockwork trap"
desc = "You shouldn't see this. File a bug report!"
clockwork_desc = "A trap that shouldn't exist, and you should report this as a bug."
var/list/wired_to
/obj/structure/destructible/clockwork/trap/Initialize()
. = ..()
wired_to = list()
/obj/structure/destructible/clockwork/trap/Destroy()
for(var/V in wired_to)
var/obj/structure/destructible/clockwork/trap/T = V
T.wired_to -= src
return ..()
/obj/structure/destructible/clockwork/trap/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "It's wired to:")
if(!wired_to.len)
to_chat(user, "Nothing.")
else
for(var/V in wired_to)
var/obj/O = V
var/distance = get_dist(src, O)
to_chat(user, "[O] ([distance == 0 ? "same tile" : "[distance] tiles [dir2text(get_dir(src, O))]"])")
/obj/structure/destructible/clockwork/trap/wrench_act(mob/living/user, obj/item/I)
if(!is_servant_of_ratvar(user))
return ..()
to_chat(user, "<span class='notice'>You break down the delicate components of [src] into brass.</span>")
I.play_tool_sound(src)
new/obj/item/stack/tile/brass(get_turf(src))
qdel(src)
return TRUE
/obj/structure/destructible/clockwork/trap/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/clockwork/slab) && is_servant_of_ratvar(user))
var/obj/item/clockwork/slab/F = I
if(!F.linking)
to_chat(user, "<span class='notice'>Beginning link. Alt-click the slab to cancel, or use it on another trap object to link the two.</span>")
F.linking = src
else
if(F.linking in wired_to)
to_chat(user, "<span class='warning'>These two objects are already connected!</span>")
return
if(F.linking.z != z)
to_chat(user, "<span class='warning'>You'd need a <b>much</b> tougher slab to link two objects in different sectors.</span>")
return
to_chat(user, "<span class='notice'>You link [F.linking] with [src].</span>")
wired_to += F.linking
F.linking.wired_to += src
F.linking = null
return
..()
/obj/structure/destructible/clockwork/trap/wirecutter_act(mob/living/user, obj/item/I)
if(!is_servant_of_ratvar(user))
return
if(!wired_to.len)
to_chat(user, "<span class='warning'>[src] has no connections!</span>")
return
to_chat(user, "<span class='notice'>You sever all connections to [src].</span>")
I.play_tool_sound(src)
for(var/V in wired_to)
var/obj/structure/destructible/clockwork/trap/T = V
T.wired_to -= src
wired_to -= T
return TRUE
/obj/structure/destructible/clockwork/trap/proc/activate()
return
//These objects send signals to normal traps to activate
/obj/structure/destructible/clockwork/trap/trigger
name = "base trap trigger"
max_integrity = 5
break_message = "<span class='warning'>The trigger breaks apart!</span>"
density = FALSE
/obj/structure/destructible/clockwork/trap/trigger/Initialize()
. = ..()
for(var/obj/structure/destructible/clockwork/trap/T in get_turf(src))
if(!istype(T, /obj/structure/destructible/clockwork/trap/trigger))
wired_to += T
T.wired_to += src
to_chat(usr, "<span class='alloy'>[src] automatically links with [T] beneath it.</span>")
/obj/structure/destructible/clockwork/trap/trigger/activate()
for(var/obj/structure/destructible/clockwork/trap/T in wired_to)
if(istype(T, /obj/structure/destructible/clockwork/trap/trigger)) //Triggers don't go off multiple times
continue
T.activate()
@@ -1,112 +0,0 @@
//Used to "declare war" against the station. The servants' equipment will be permanently supercharged, and the Ark given extra time to prepare.
//This will send an announcement to the station, meaning that they will be warned very early in advance about the impending attack.
/obj/structure/destructible/clockwork/heralds_beacon
name = "herald's beacon"
desc = "An imposing spire formed of brass, with a thrumming gemstone at its peak."
clockwork_desc = "A massively-powerful beacon. If enough servants decide to activate it, it will send an incredibly large energy pulse to the Ark, \
permanently empowering many clockwork objects and reducing all power costs by 50%, but alerting the crew to your presence. It doesn't have enough \
energy to sustain itself for long, and if not activated within five minutes, it will permanently shut down."
icon_state = "interdiction_lens"
break_message = "<span class='warning'>The beacon crackles with power before collapsing into pieces!</span>"
max_integrity = 250
light_color = "#EF078E"
var/time_remaining = 300 //Amount of seconds left to vote on whether or not to activate the beacon
var/list/voters //People who have voted to activate the beacon
var/votes_needed = 0 //How many votes are needed to activate the beacon
var/available = FALSE //If the beacon can be used
/obj/structure/destructible/clockwork/heralds_beacon/Initialize()
. = ..()
voters = list()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/Destroy()
STOP_PROCESSING(SSprocessing, src)
. = ..()
/obj/structure/destructible/clockwork/heralds_beacon/process()
if(!available)
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult))
available = TRUE
else
return
if(!SSticker.mode.servants_of_ratvar.len)
return
if(!votes_needed)
var/servants = SSticker.mode.servants_of_ratvar.len
if(servants)
votes_needed = round(servants * 0.66)
time_remaining--
if(!time_remaining)
hierophant_message("<span class='bold sevtug_small'>[src] has lost its power, and can no longer be activated.</span>")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/blind.ogg', 50, FALSE)
available = FALSE
icon_state = "interdiction_lens_unwrenched"
STOP_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/examine(mob/user)
..()
if(isobserver(user) || is_servant_of_ratvar(user))
if(!available)
if(!GLOB.ratvar_approaches)
to_chat(user, "<span class='bold alloy'>It can no longer be activated.</span>")
else
to_chat(user, "<span class='bold neovgre_small'>It has been activated!</span>")
else
to_chat(user, "<span class='brass'>There are <b>[time_remaining]</b> second[time_remaining != 1 ? "s" : ""] remaining to vote.</span>")
to_chat(user, "<span class='big brass'>There are <b>[voters.len]/[votes_needed]</b> votes to activate the beacon!</span>")
/obj/structure/destructible/clockwork/heralds_beacon/attack_hand(mob/living/user)
. = ..()
if(.)
return
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='notice'>You can tell how powerful [src] is; you know better than to touch it.</span>")
return
if(!available)
to_chat(user, "<span class='danger'>You can no longer vote with [src].</span>")
return
var/voting = !(user.key in voters)
if(alert(user, "[voting ? "Cast a" : "Undo your"] vote to activate the beacon?", "Herald's Beacon", "Change Vote", "Cancel") == "Cancel")
return
if(!user.canUseTopic(src) || !is_servant_of_ratvar(user) || !available)
return
if(voting)
if(user.key in voters)
return
voters += user.key
else
if(!(user.key in voters))
return
voters -= user.key
var/votes_left = votes_needed - voters.len
message_admins("[ADMIN_LOOKUPFLW(user)] has [voting ? "voted" : "undone their vote"] to activate [src]! [ADMIN_JMP(user)]")
hierophant_message("<span class='brass'><b>[user.real_name]</b> has [voting ? "voted" : "undone their vote"] to activate [src]! The beacon needs [votes_left] more votes to activate.")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/clockwork/fellowship_armory.ogg', 50, FALSE)
if(!votes_left)
herald_the_justiciar()
/obj/structure/destructible/clockwork/heralds_beacon/proc/herald_the_justiciar()
priority_announce("A powerful group of fanatical zealots following the cause of Ratvar have brazenly sacrificed stealth for power, and dare anyone \
to try and stop them.", title = "The Justiciar Comes", sound = 'sound/magic/clockwork/ark_activation.ogg')
GLOB.ratvar_approaches = TRUE
available = FALSE
STOP_PROCESSING(SSprocessing, src)
icon_state = "interdiction_lens_active"
hierophant_message("<span class='big bold brass'>The beacon's activation has given your team great power! Many of your objects are permanently empowered!</span>")
for(var/mob/living/simple_animal/hostile/clockwork/C in GLOB.all_clockwork_mobs)
if(C.stat == DEAD)
continue
C.update_values()
to_chat(C, C.empower_string)
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(is_servant_of_ratvar(H))
to_chat(H, "<span class='bold alloy'>The beacon'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>")
H.set_species(/datum/species/golem/clockwork/no_scrap)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
G.grace_period = FALSE //no grace period if we've declared war
G.recalls_remaining++
@@ -1,163 +0,0 @@
//Ocular warden: Low-damage, low-range turret. Deals constant damage to whoever it makes eye contact with.
/obj/structure/destructible/clockwork/ocular_warden
name = "ocular warden"
desc = "A large brass eye with tendrils trailing below it and a wide red iris."
clockwork_desc = "A fragile turret which will automatically attack nearby unrestrained non-Servants that can see it."
icon_state = "ocular_warden"
unanchored_icon = "ocular_warden_unwrenched"
max_integrity = 25
construction_value = 15
layer = WALL_OBJ_LAYER
break_message = "<span class='warning'>The warden's eye gives a glare of utter hate before falling dark!</span>"
debris = list(/obj/item/clockwork/component/belligerent_eye/blind_eye = 1)
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/damage_per_tick = 3
var/sight_range = 3
var/atom/movable/target
var/list/idle_messages = list(" sulkily glares around.", " lazily drifts from side to side.", " looks around for something to burn.", " slowly turns in circles.")
/obj/structure/destructible/clockwork/ocular_warden/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
/obj/structure/destructible/clockwork/ocular_warden/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()
/obj/structure/destructible/clockwork/ocular_warden/examine(mob/user)
..()
to_chat(user, "<span class='brass'>[target ? "<b>It's fixated on [target]!</b>" : "Its gaze is wandering aimlessly."]</span>")
/obj/structure/destructible/clockwork/ocular_warden/hulk_damage()
return 25
/obj/structure/destructible/clockwork/ocular_warden/can_be_unfasten_wrench(mob/user, silent)
if(!anchored)
for(var/obj/structure/destructible/clockwork/ocular_warden/W in orange(OCULAR_WARDEN_EXCLUSION_RANGE, src))
if(W.anchored)
if(!silent)
to_chat(user, "<span class='neovgre'>You sense another ocular warden too near this location. Activating this one this close would cause them to fight.</span>")
return FAILED_UNFASTEN
return SUCCESSFUL_UNFASTEN
/obj/structure/destructible/clockwork/ocular_warden/ratvar_act()
..()
if(GLOB.ratvar_awakens)
damage_per_tick = 10
sight_range = 6
else
damage_per_tick = initial(damage_per_tick)
sight_range = initial(sight_range)
/obj/structure/destructible/clockwork/ocular_warden/process()
if(!anchored)
lose_target()
return
var/list/validtargets = acquire_nearby_targets()
if(target)
if(!(target in validtargets))
lose_target()
else
if(isliving(target))
var/mob/living/L = target
if(!L.anti_magic_check(chargecost = 0))
if(isrevenant(L))
var/mob/living/simple_animal/revenant/R = L
if(R.revealed)
R.unreveal_time += 2
else
R.reveal(10)
if(prob(50))
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',75 * get_efficiency_mod(),1)
else
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',75 * get_efficiency_mod(),1)
L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar'Sian cultists take additional damage
if(GLOB.ratvar_awakens && L)
L.adjust_fire_stacks(damage_per_tick)
L.IgniteMob()
else if(ismecha(target))
var/obj/mecha/M = target
M.take_damage(damage_per_tick * get_efficiency_mod(), BURN, "melee", 1, get_dir(src, M))
new /obj/effect/temp_visual/ratvar/ocular_warden(get_turf(target))
setDir(get_dir(get_turf(src), get_turf(target)))
if(!target)
if(validtargets.len)
target = pick(validtargets)
playsound(src,'sound/machines/clockcult/ocularwarden-target.ogg',50,1)
visible_message("<span class='warning'>[src] swivels to face [target]!</span>")
if(isliving(target))
var/mob/living/L = target
to_chat(L, "<span class='neovgre'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [GLOB.ratvar_awakens ? "melts you alive" : "burns you"]!</span>")
else if(ismecha(target))
var/obj/mecha/M = target
to_chat(M.occupant, "<span class='neovgre'>\"I SEE YOU!\"</span>" )
else if(prob(0.5)) //Extremely low chance because of how fast the subsystem it uses processes
if(prob(50))
visible_message("<span class='notice'>[src][pick(idle_messages)]</span>")
else
setDir(pick(GLOB.cardinals))//Random rotation
/obj/structure/destructible/clockwork/ocular_warden/proc/acquire_nearby_targets()
. = list()
for(var/mob/living/L in viewers(sight_range, src)) //Doesn't attack the blind
var/obj/item/storage/book/bible/B = L.bible_check()
if(B)
if(!(B.resistance_flags & ON_FIRE))
to_chat(L, "<span class='warning'>Your [B.name] bursts into flames!</span>")
for(var/obj/item/storage/book/bible/BI in L.GetAllContents())
if(!(BI.resistance_flags & ON_FIRE))
BI.fire_act()
continue
if(is_servant_of_ratvar(L) || (HAS_TRAIT(L, TRAIT_BLIND)) || L.anti_magic_check(TRUE, TRUE) || L.incapacitated(TRUE))
continue
if (iscarbon(L))
var/mob/living/carbon/c = L
if (istype(c.handcuffed,/obj/item/restraints/handcuffs/clockwork))
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
if(("ratvar" in H.faction) || (!H.mind && "neutral" in H.faction))
continue
if(ismegafauna(H) || (!H.mind && H.AIStatus == AI_OFF))
continue
else if(isrevenant(L))
var/mob/living/simple_animal/revenant/R = L
if(R.stasis) //Don't target any revenants that are respawning
continue
else if(!L.mind)
continue
. += L
var/list/viewcache = list()
for(var/N in GLOB.mechas_list)
var/obj/mecha/M = N
if(get_dist(M, src) <= sight_range && M.occupant && !is_servant_of_ratvar(M.occupant))
if(!length(viewcache))
for (var/obj/Z in view(sight_range, src))
viewcache += Z
if (M in viewcache)
. += M
/obj/structure/destructible/clockwork/ocular_warden/proc/lose_target()
if(!target)
return 0
target = null
visible_message("<span class='warning'>[src] settles and seems almost disappointed.</span>")
return 1
/obj/structure/destructible/clockwork/ocular_warden/get_efficiency_mod()
if(GLOB.ratvar_awakens)
return 2
. = 1
if(target)
for(var/turf/T in getline(src, target))
if(T.density)
. -= 0.1
continue
for(var/obj/structure/O in T)
if(O != src && O.density)
. -= 0.1
break
. -= (get_dist(src, target) * 0.05)
. = max(., 0.1) //The lowest damage a warden can do is 10% of its normal amount (0.25 by default)
@@ -1,61 +0,0 @@
//Used by the Taunting Tirade scripture as a trail.
/obj/structure/destructible/clockwork/taunting_trail
name = "strange smoke"
desc = "A cloud of purple smoke."
clockwork_desc = "A cloud of purple smoke that confuses and knocks down non-Servants that enter it."
gender = PLURAL
max_integrity = 5
density = TRUE
color = list("#AF0AAF", "#AF0AAF", "#AF0AAF", rgb(0,0,0))
icon = 'icons/effects/effects.dmi'
icon_state = "smoke"
break_message = null
break_sound = 'sound/magic/teleport_app.ogg'
debris = list()
var/timerid
/obj/structure/destructible/clockwork/taunting_trail/Initialize()
. = ..()
timerid = QDEL_IN(src, 15)
var/obj/structure/destructible/clockwork/taunting_trail/Tt = locate(/obj/structure/destructible/clockwork/taunting_trail) in loc
if(Tt && Tt != src)
if(!step(src, pick(GLOB.alldirs)))
qdel(Tt)
else
for(var/obj/structure/destructible/clockwork/taunting_trail/TT in loc)
if(TT != src)
qdel(TT)
setDir(pick(GLOB.cardinals))
transform = matrix()*1.3
animate(src, alpha = 100, time = 15)
/obj/structure/destructible/clockwork/taunting_trail/Destroy()
deltimer(timerid)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(src, 'sound/items/welder.ogg', 50, 1)
/obj/structure/destructible/clockwork/taunting_trail/CanPass(atom/movable/mover, turf/target)
return TRUE
/obj/structure/destructible/clockwork/taunting_trail/Crossed(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/Bumped(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/Bump(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/proc/affect_mob(mob/living/L)
if(istype(L) && !is_servant_of_ratvar(L))
if(!L.anti_magic_check(chargecost = 0))
L.confused = min(L.confused + 15, 50)
L.dizziness = min(L.dizziness + 15, 50)
if(L.confused >= 25)
L.Knockdown(FLOOR(L.confused * 0.8, 1))
take_damage(max_integrity)
@@ -1,17 +0,0 @@
//Pressure sensor: Activates when stepped on.
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor
name = "pressure sensor"
desc = "A thin plate of brass, barely visible but clearly distinct."
clockwork_desc = "A trigger that will activate when a non-servant runs across it."
max_integrity = 5
icon_state = "pressure_sensor"
alpha = 50
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/Crossed(atom/movable/AM)
if(isliving(AM) && !is_servant_of_ratvar(AM))
var/mob/living/L = AM
if(L.stat || L.m_intent == MOVE_INTENT_WALK || L.movement_type & (FLYING|FLOATING))
return
audible_message("<i>*click*</i>")
playsound(src, 'sound/items/screwdriver2.ogg', 50, TRUE)
activate()
@@ -1,23 +0,0 @@
//This doesn't function like a "trap" in of itself, but obscures vision when active.
/obj/structure/destructible/clockwork/trap/steam_vent
name = "steam vent"
desc = "Some wired slats embedded in the floor. They feel warm to the touch."
icon_state = "steam_vent_0"
clockwork_desc = "When active, these vents will billow out clouds of excess steam from Reebe, obscuring vision."
break_message = "<span class='warning'>The vent snaps and collapses!</span>"
max_integrity = 100
density = FALSE
/obj/structure/destructible/clockwork/trap/steam_vent/activate()
opacity = !opacity
icon_state = "steam_vent_[opacity]"
if(opacity)
playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50, TRUE)
else
playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE)
/obj/structure/destructible/clockwork/trap/steam_vent/Crossed(atom/movable/AM)
if(isliving(AM) && opacity)
var/mob/living/L = AM
L.adjust_fire_stacks(-1) //It's wet!
@@ -1,73 +0,0 @@
//A massive gear, effectively a girder for clocks.
/obj/structure/destructible/clockwork/wall_gear
name = "massive gear"
icon_state = "wall_gear"
unanchored_icon = "wall_gear"
climbable = TRUE
max_integrity = 100
construction_value = 3
desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, or just climb over it."
break_message = "<span class='warning'>The gear breaks apart into shards of alloy!</span>"
debris = list(/obj/item/clockwork/alloy_shards/large = 1, \
/obj/item/clockwork/alloy_shards/medium = 4, \
/obj/item/clockwork/alloy_shards/small = 2) //slightly more debris than the default, totals 26 alloy
/obj/structure/destructible/clockwork/wall_gear/displaced
anchored = FALSE
/obj/structure/destructible/clockwork/wall_gear/Initialize()
. = ..()
new /obj/effect/temp_visual/ratvar/gear(get_turf(src))
/obj/structure/destructible/clockwork/wall_gear/emp_act(severity)
return
/obj/structure/destructible/clockwork/wall_gear/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wrench))
default_unfasten_wrench(user, I, 10)
return 1
else if(istype(I, /obj/item/screwdriver))
if(anchored)
to_chat(user, "<span class='warning'>[src] needs to be unsecured to disassemble it!</span>")
else
user.visible_message("<span class='warning'>[user] starts to disassemble [src].</span>", "<span class='notice'>You start to disassemble [src]...</span>")
if(I.use_tool(src, user, 30, volume=100) && !anchored)
to_chat(user, "<span class='notice'>You disassemble [src].</span>")
deconstruct(TRUE)
return 1
else if(istype(I, /obj/item/stack/tile/brass))
var/obj/item/stack/tile/brass/W = I
if(W.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one brass sheet to do this!</span>")
return
var/turf/T = get_turf(src)
if(iswallturf(T))
to_chat(user, "<span class='warning'>There is already a wall present!</span>")
return
if(!isfloorturf(T))
to_chat(user, "<span class='warning'>A floor must be present to build a [anchored ? "false ":""]wall!</span>")
return
if(locate(/obj/structure/falsewall) in T.contents)
to_chat(user, "<span class='warning'>There is already a false wall present!</span>")
return
to_chat(user, "<span class='notice'>You start adding [W] to [src]...</span>")
if(do_after(user, 20, target = src))
var/brass_floor = FALSE
if(istype(T, /turf/open/floor/clockwork)) //if the floor is already brass, costs less to make(conservation of masssssss)
brass_floor = TRUE
if(W.use(2 - brass_floor))
if(anchored)
T.PlaceOnTop(/turf/closed/wall/clockwork)
else
T.PlaceOnTop(/turf/open/floor/clockwork)
new /obj/structure/falsewall/brass(T)
qdel(src)
else
to_chat(user, "<span class='warning'>You need more brass to make a [anchored ? "false ":""]wall!</span>")
return 1
return ..()
/obj/structure/destructible/clockwork/wall_gear/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1) && disassembled)
new /obj/item/stack/tile/brass(loc, 3)
return ..()
-440
View File
@@ -1,440 +0,0 @@
#define SUMMON_POSSIBILITIES 3
/datum/antagonist/cult
name = "Cultist"
roundend_category = "cultists"
antagpanel_category = "Cult"
antag_moodlet = /datum/mood_event/cult
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
var/datum/action/innate/cult/blood_magic/magic = new
job_rank = ROLE_CULTIST
var/ignore_implant = FALSE
var/give_equipment = FALSE
var/datum/team/cult/cult_team
/datum/antagonist/cult/get_team()
return cult_team
/datum/antagonist/cult/create_team(datum/team/cult/new_team)
if(!new_team)
//todo remove this and allow admin buttons to create more than one cult
for(var/datum/antagonist/cult/H in GLOB.antagonists)
if(!H.owner)
continue
if(H.cult_team)
cult_team = H.cult_team
return
cult_team = new /datum/team/cult
cult_team.setup_objectives()
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
cult_team = new_team
/datum/antagonist/cult/proc/add_objectives()
objectives |= cult_team.objectives
/datum/antagonist/cult/Destroy()
QDEL_NULL(communion)
QDEL_NULL(vote)
return ..()
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
. = ..()
if(. && !ignore_implant)
. = is_convertable_to_cult(new_owner.current,cult_team)
/datum/antagonist/cult/greet()
to_chat(owner, "<span class='userdanger'>You are a member of the cult!</span>")
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
owner.announce_objectives()
/datum/antagonist/cult/on_gain()
. = ..()
var/mob/living/current = owner.current
add_objectives()
if(give_equipment)
equip_cultist(TRUE)
SSticker.mode.cult += owner // Only add after they've been given objectives
SSticker.mode.update_cult_icons_added(owner)
current.log_message("has been converted to the cult of Nar'Sie!", LOG_ATTACK, color="#960000")
if(cult_team.blood_target && cult_team.blood_target_image && current.client)
current.client.images += cult_team.blood_target_image
/datum/antagonist/cult/proc/equip_cultist(metal=TRUE)
var/mob/living/carbon/H = owner.current
if(!istype(H))
return
if (owner.assigned_role == "Clown")
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
H.dna.remove_mutation(CLOWNMUT)
. += cult_give_item(/obj/item/melee/cultblade/dagger, H)
if(metal)
. += cult_give_item(/obj/item/stack/sheet/runed_metal/ten, H)
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
var/list/slots = list(
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/T = new item_path(mob)
var/item_name = initial(item_path.name)
var/where = mob.equip_in_one_of_slots(T, slots)
if(!where)
to_chat(mob, "<span class='userdanger'>Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).</span>")
return 0
else
to_chat(mob, "<span class='danger'>You have a [item_name] in your [where].</span>")
if(where == "backpack")
SEND_SIGNAL(mob.back, COMSIG_TRY_STORAGE_SHOW, mob)
return TRUE
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
current.faction |= "cult"
current.grant_language(/datum/language/narsie)
if(!cult_team.cult_master)
vote.Grant(current)
communion.Grant(current)
if(ishuman(current))
magic.Grant(current)
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
if(cult_team.cult_risen)
cult_team.rise(current)
if(cult_team.cult_ascendent)
cult_team.ascend(current)
/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
current.faction -= "cult"
current.remove_language(/datum/language/narsie)
vote.Remove(current)
communion.Remove(current)
magic.Remove(current)
current.clear_alert("bloodsense")
if(ishuman(current))
var/mob/living/carbon/human/H = current
H.eye_color = initial(H.eye_color)
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
REMOVE_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
H.update_body()
H.cut_overlays()
H.regenerate_icons()
/datum/antagonist/cult/on_removal()
SSticker.mode.cult -= owner
SSticker.mode.update_cult_icons_removed(owner)
if(!silent)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!</span>", null, null, null, owner.current)
to_chat(owner.current, "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.</span>")
owner.current.log_message("has renounced the cult of Nar'Sie!", LOG_ATTACK, color="#960000")
if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
owner.current.client.images -= cult_team.blood_target_image
. = ..()
/datum/antagonist/cult/admin_add(datum/mind/new_owner,mob/admin)
give_equipment = FALSE
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has cult'ed [new_owner.current].")
log_admin("[key_name(admin)] has cult'ed [new_owner.current].")
/datum/antagonist/cult/admin_remove(mob/user)
message_admins("[key_name_admin(user)] has decult'ed [owner.current].")
log_admin("[key_name(user)] has decult'ed [owner.current].")
SSticker.mode.remove_cultist(owner,silent=TRUE) //disgusting
/datum/antagonist/cult/get_admin_commands()
. = ..()
.["Dagger"] = CALLBACK(src,.proc/admin_give_dagger)
.["Dagger and Metal"] = CALLBACK(src,.proc/admin_give_metal)
/datum/antagonist/cult/proc/admin_give_dagger(mob/admin)
if(!equip_cultist(FALSE))
to_chat(admin, "<span class='danger'>Spawning dagger failed!</span>")
/datum/antagonist/cult/proc/admin_give_metal(mob/admin)
if (!equip_cultist(TRUE))
to_chat(admin, "<span class='danger'>Spawning runed metal failed!</span>")
/datum/antagonist/cult/master
ignore_implant = TRUE
show_in_antagpanel = FALSE //Feel free to add this later
var/datum/action/innate/cult/master/finalreck/reckoning = new
var/datum/action/innate/cult/master/cultmark/bloodmark = new
var/datum/action/innate/cult/master/pulse/throwing = new
/datum/antagonist/cult/master/Destroy()
QDEL_NULL(reckoning)
QDEL_NULL(bloodmark)
QDEL_NULL(throwing)
return ..()
/datum/antagonist/cult/master/on_gain()
. = ..()
var/mob/living/current = owner.current
set_antag_hud(current, "cultmaster")
/datum/antagonist/cult/master/greet()
to_chat(owner.current, "<span class='cultlarge'>You are the cult's Master</span>. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of summoning the entire living cult to your location <b><i>once</i></b>.")
to_chat(owner.current, "Use these abilities to direct the cult to victory at any cost.")
/datum/antagonist/cult/master/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
if(!cult_team.reckoning_complete)
reckoning.Grant(current)
bloodmark.Grant(current)
throwing.Grant(current)
current.update_action_buttons_icon()
current.apply_status_effect(/datum/status_effect/cult_master)
if(cult_team.cult_risen)
cult_team.rise(current)
if(cult_team.cult_ascendent)
cult_team.ascend(current)
/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
reckoning.Remove(current)
bloodmark.Remove(current)
throwing.Remove(current)
current.update_action_buttons_icon()
current.remove_status_effect(/datum/status_effect/cult_master)
if(ishuman(current))
var/mob/living/carbon/human/H = current
H.eye_color = initial(H.eye_color)
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
REMOVE_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
H.cut_overlays()
H.regenerate_icons()
/datum/team/cult
name = "Cult"
var/blood_target
var/image/blood_target_image
var/blood_target_reset_timer
var/cult_vote_called = FALSE
var/mob/living/cult_master
var/reckoning_complete = FALSE
var/cult_risen = FALSE
var/cult_ascendent = FALSE
/datum/team/cult/New()
. = ..()
START_PROCESSING(SSprocessing, src)
/datum/team/cult/Destroy()
STOP_PROCESSING(SSprocessing, src)
return ..()
/datum/team/cult/process()
if(SSticker.current_state == GAME_STATE_FINISHED)
return
var/datum/objective/sacrifice/sac_objective = locate() in objectives
if(!sac_objective || sac_objective.check_completion())
return
var/datum/mind/sacrificial = sac_objective.get_target()
var/mob/living/sac_current = sacrificial.current
if(!sacrificial || !sac_current) //target is gone for good but not sacrified.
sort_sacrifice(TRUE)
return
if(QDELETED(sac_objective.target_current) || sac_objective.target_current != sac_current) //target is now a different mob (monkey, simple mob)
sac_objective.sac_image = sac_current.get_sac_image()
sac_objective.target_current = sac_current
sac_objective.update_explanation_text()
/datum/team/cult/proc/check_size()
if(cult_ascendent)
return
var/alive = 0
var/cultplayers = 0
for(var/I in GLOB.player_list)
var/mob/M = I
if(M.stat != DEAD)
if(iscultist(M))
++cultplayers
else
++alive
var/ratio = cultplayers/alive
if(ratio > CULT_RISEN && !cult_risen)
for(var/datum/mind/B in members)
if(B.current)
SEND_SOUND(B.current, 'sound/hallucinations/i_see_you2.ogg')
to_chat(B.current, "<span class='cultlarge'>The veil weakens as your cult grows, your eyes begin to glow...")
addtimer(CALLBACK(src, .proc/rise, B.current), 200)
cult_risen = TRUE
if(ratio > CULT_ASCENDENT && !cult_ascendent)
for(var/datum/mind/B in members)
if(B.current)
SEND_SOUND(B.current, 'sound/hallucinations/im_here1.ogg')
to_chat(B.current, "<span class='cultlarge'>Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!")
addtimer(CALLBACK(src, .proc/ascend, B.current), 200)
cult_ascendent = TRUE
/datum/team/cult/proc/rise(cultist)
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
H.eye_color = "f00"
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
ADD_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
H.update_body()
/datum/team/cult/proc/ascend(cultist)
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir)
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6")
H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -BODY_FRONT_LAYER))
/datum/team/cult/proc/setup_objectives()
//SAC OBJECTIVE , todo: move this to objective internals
sort_sacrifice()
//SUMMON OBJECTIVE
var/datum/objective/eldergod/summon_objective = new()
summon_objective.team = src
objectives += summon_objective
/datum/team/cult/proc/sort_sacrifice(replacement = FALSE)
var/list/target_candidates = list()
var/datum/objective/sacrifice/sac_objective = locate() in GLOB.objectives
if(!sac_objective)
sac_objective = new
sac_objective.team = src
for(var/mob/living/carbon/human/player in GLOB.player_list)
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD)
target_candidates += player.mind
if(!length(target_candidates))
message_admins("Cult Sacrifice: Could not find unconvertible target, checking for convertible target.")
for(var/mob/living/carbon/human/player in GLOB.player_list)
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD)
target_candidates += player.mind
listclearnulls(target_candidates)
if(!LAZYLEN(target_candidates))
message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. Proceeding to next stage!")
sac_objective.sacced = TRUE
return
var/datum/mind/new_target = pick(target_candidates)
if(replacement)
for(var/datum/mind/H in members)
if(H.current)
to_chat(H.current, "<span class='danger'>Nar'Sie</span> murmurs, <span class='cultlarge'>[sac_objective.target] is beyond your reach. Sacrifice [new_target] instead...</span></span>")
sac_objective.target = new_target
sac_objective.target_current = new_target.current
sac_objective.update_explanation_text()
sac_objective.sac_image = sac_objective.target_current.get_sac_image()
objectives += sac_objective
/mob/proc/get_sac_image()
var/icon/reshape = icon(icon, icon_state, SOUTH)
reshape.Shift(SOUTH, 4)
reshape.Shift(EAST, 1)
reshape.Crop(7,4,26,31)
reshape.Crop(-5,-3,26,30)
return reshape
/mob/living/carbon/human/get_sac_image()
var/datum/job/sacjob = SSjob.GetJob(mind.assigned_role)
var/datum/preferences/sacface = client.prefs
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface, list(SOUTH))
reshape.Shift(SOUTH, 4)
reshape.Shift(EAST, 1)
reshape.Crop(7,4,26,31)
reshape.Crop(-5,-3,26,30)
return reshape
/datum/objective/sacrifice
var/sacced = FALSE
var/sac_image
var/mob/living/target_current
/datum/objective/sacrifice/check_completion()
return sacced || completed
/datum/objective/sacrifice/update_explanation_text()
if(target)
explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking a Sacrifice rune with [target.p_them()] on it and three acolytes around it."
else
explanation_text = "The veil has already been weakened here, proceed to the final objective."
/datum/objective/eldergod
var/summoned = FALSE
var/list/summon_spots = list()
/datum/objective/eldergod/New()
..()
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)
summon_spots += summon
sanity++
update_explanation_text()
/datum/objective/eldergod/update_explanation_text()
explanation_text = "Summon Nar'Sie by invoking the rune 'Summon Nar'Sie'. <b>The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
/datum/objective/eldergod/check_completion()
return summoned || completed
/datum/team/cult/proc/check_cult_victory()
for(var/datum/objective/O in objectives)
if(!O.check_completion())
return FALSE
return TRUE
/datum/team/cult/roundend_report()
var/list/parts = list()
if(check_cult_victory())
parts += "<span class='greentext big'>The cult has succeeded! Nar'Sie has snuffed out another torch in the void!</span>"
else
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
if(objectives.len)
parts += "<b>The cultists' objectives were:</b>"
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
count++
if(members.len)
parts += "<span class='header'>The cultists were:</span>"
parts += printplayerlist(members)
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
/datum/team/cult/is_gamemode_hero()
return SSticker.mode.name == "cult"
File diff suppressed because it is too large Load Diff
-151
View File
@@ -1,151 +0,0 @@
/*
This file contains the cult dagger and rune list code
*/
/obj/item/melee/cultblade/dagger/Initialize()
. = ..()
if(!LAZYLEN(GLOB.rune_types))
GLOB.rune_types = list()
var/static/list/non_revealed_runes = (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
for(var/i_can_do_loops_now_thanks_remie in non_revealed_runes)
var/obj/effect/rune/R = i_can_do_loops_now_thanks_remie
GLOB.rune_types[initial(R.cultist_name)] = R //Uses the cultist name for displaying purposes
/obj/item/melee/cultblade/dagger/examine(mob/user)
..()
if(iscultist(user) || isobserver(user))
to_chat(user, "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar'Sie.</span>")
to_chat(user, "<span class='cult'>Striking a cult structure will unanchor or reanchor it.</span>")
to_chat(user, "<span class='cult'>Striking another cultist with it will purge holy water from them.</span>")
to_chat(user, "<span class='cult'>Striking a noncultist, however, will tear their flesh.</span>")
/obj/item/melee/cultblade/dagger/attack(mob/living/M, mob/living/user)
if(iscultist(M))
if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
to_chat(user, "<span class='cult'>You remove the taint from [M].</span>" )
var/holy2unholy = M.reagents.get_reagent_amount("holywater")
M.reagents.del_reagent("holywater")
M.reagents.add_reagent("unholywater",holy2unholy)
log_combat(user, M, "smacked", src, " removing the holy water from them")
return FALSE
. = ..()
/obj/item/melee/cultblade/dagger/attack_self(mob/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>[src] is covered in unintelligible shapes and markings.</span>")
return
scribe_rune(user)
/obj/item/melee/cultblade/dagger/proc/scribe_rune(mob/living/user)
var/turf/Turf = get_turf(user)
var/chosen_keyword
var/obj/effect/rune/rune_to_scribe
var/entered_rune_name
var/list/shields = list()
var/area/A = get_area(src)
var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(!user_antag)
return
if(!check_rune_turf(Turf, user))
return
entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in GLOB.rune_types
if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user))
return
rune_to_scribe = GLOB.rune_types[entered_rune_name]
if(!rune_to_scribe)
return
if(initial(rune_to_scribe.req_keyword))
chosen_keyword = stripped_input(user, "Enter a keyword for the new rune.", "Words of Power")
if(!chosen_keyword)
scribe_rune(user) //Go back a menu!
return
Turf = get_turf(user) //we may have moved. adjust as needed...
A = get_area(src)
if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user))
return
if(ispath(rune_to_scribe, /obj/effect/rune/summon) && (!is_station_level(Turf.z) || A.map_name == "Space"))
to_chat(user, "<span class='cultitalic'><b>The veil is not weak enough here to summon a cultist, you must be on station!</b></span>")
return
if(ispath(rune_to_scribe, /obj/effect/rune/apocalypse))
if((world.time - SSticker.round_start_time) <= 6000)
var/wait = 6000 - (world.time - SSticker.round_start_time)
to_chat(user, "<span class='cult italic'>The veil is not yet weak enough for this rune - it will be available in [DisplayTimeText(wait)].</span>")
return
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
if(!(A in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site (where Nar'Sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
return
if(summon_objective.summon_spots.len < 2)
to_chat(user, "<span class='cultlarge'>Only one ritual site remains - it must be reserved for the final summoning!</span>")
return
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives
if(!summon_objective)
to_chat(user, "<span class='warning'>Nar'Sie does not wish to be summoned!</span>")
return
if(sac_objective && !sac_objective.check_completion())
to_chat(user, "<span class='warning'>The sacrifice is not complete. The portal would lack the power to open if you tried!</span>")
return
if(summon_objective.check_completion())
to_chat(user, "<span class='cultlarge'>\"I am already here. There is no need to try to summon me now.\"</span>")
return
if(!(A in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!</span>")
return
var/confirm_final = alert(user, "This is the FINAL step to summon Nar'Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar'Sie!", "No")
if(confirm_final == "No")
to_chat(user, "<span class='cult'>You decide to prepare further before scribing the rune.</span>")
return
Turf = get_turf(user)
A = get_area(src)
if(!(A in summon_objective.summon_spots)) // Check again to make sure they didn't move
to_chat(user, "<span class='cultlarge'>The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!</span>")
return
priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", "spanomalies")
for(var/B in spiral_range_turfs(1, user, 1))
var/obj/structure/emergency_shield/sanguine/N = new(B)
shields += N
user.visible_message("<span class='warning'>[user] [user.blood_volume ? "cuts open [user.p_their()] arm and begins writing in [user.p_their()] own blood":"begins sketching out a strange design"]!</span>", \
"<span class='cult'>You [user.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.</span>")
if(user.blood_volume)
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
if(istype(get_turf(user), /turf/open/floor/engine/cult))
scribe_mod *= 0.5
if(!do_after(user, scribe_mod, target = get_turf(user)))
for(var/V in shields)
var/obj/structure/emergency_shield/sanguine/S = V
if(S && !QDELETED(S))
qdel(S)
return
if(!check_rune_turf(Turf, user))
return
user.visible_message("<span class='warning'>[user] creates a strange circle[user.blood_volume ? " in [user.p_their()] own blood":""].</span>", \
"<span class='cult'>You finish drawing the arcane markings of the Geometer.</span>")
for(var/V in shields)
var/obj/structure/emergency_shield/S = V
if(S && !QDELETED(S))
qdel(S)
var/obj/effect/rune/R = new rune_to_scribe(Turf, chosen_keyword)
R.add_mob_blood(user)
to_chat(user, "<span class='cult'>The [lowertext(R.cultist_name)] rune [R.cultist_desc]</span>")
SSblackbox.record_feedback("tally", "cult_runes_scribed", 1, R.cultist_name)
/obj/item/melee/cultblade/dagger/proc/check_rune_turf(turf/T, mob/user)
if(isspaceturf(T))
to_chat(user, "<span class='warning'>You cannot scribe runes in space!</span>")
return FALSE
if(locate(/obj/effect/rune) in T)
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))
to_chat(user, "<span class='warning'>The veil is not weak enough here.</span>")
return FALSE
return TRUE
File diff suppressed because it is too large Load Diff
-570
View File
@@ -1,570 +0,0 @@
#define BLOOD_THRESHOLD 3 //How many souls are needed per stage.
#define TRUE_THRESHOLD 7
#define ARCH_THRESHOLD 12
#define BASIC_DEVIL 0
#define BLOOD_LIZARD 1
#define TRUE_DEVIL 2
#define ARCH_DEVIL 3
#define LOSS_PER_DEATH 2
#define SOULVALUE soulsOwned.len-reviveNumber
#define DEVILRESURRECTTIME 600
GLOBAL_LIST_EMPTY(allDevils)
GLOBAL_LIST_INIT(lawlorify, list (
LORE = list(
OBLIGATION_FOOD = "This devil seems to always offer its victims food before slaughtering them.",
OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.",
OBLIGATION_DANCEOFF = "This devil will never turn down a dance off.",
OBLIGATION_GREET = "This devil seems to only be able to converse with people it knows the name of.",
OBLIGATION_PRESENCEKNOWN = "This devil seems to be unable to attack from stealth.",
OBLIGATION_SAYNAME = "He will always chant his name upon killing someone.",
OBLIGATION_ANNOUNCEKILL = "This devil always loudly announces his kills for the world to hear.",
OBLIGATION_ANSWERTONAME = "This devil always responds to his truename.",
BANE_SILVER = "Silver seems to gravely injure this devil.",
BANE_SALT = "Throwing salt at this devil will hinder his ability to use infernal powers temporarily.",
BANE_LIGHT = "Bright flashes will disorient the devil, likely causing him to flee.",
BANE_IRON = "Cold iron will slowly injure him, until he can purge it from his system.",
BANE_WHITECLOTHES = "Wearing clean white clothing will help ward off this devil.",
BANE_HARVEST = "Presenting the labors of a harvest will disrupt the devil.",
BANE_TOOLBOX = "That which holds the means of creation also holds the means of the devil's undoing.",
BAN_HURTWOMAN = "This devil seems to prefer hunting men.",
BAN_CHAPEL = "This devil avoids holy ground.",
BAN_HURTPRIEST = "The annointed clergy appear to be immune to his powers.",
BAN_AVOIDWATER = "The devil seems to have some sort of aversion to water, though it does not appear to harm him.",
BAN_STRIKEUNCONSCIOUS = "This devil only shows interest in those who are awake.",
BAN_HURTLIZARD = "This devil will not strike a lizardman first.",
BAN_HURTANIMAL = "This devil avoids hurting animals.",
BANISH_WATER = "To banish the devil, you must infuse its body with holy water.",
BANISH_COFFIN = "This devil will return to life if its remains are not placed within a coffin.",
BANISH_FORMALDYHIDE = "To banish the devil, you must inject its lifeless body with embalming fluid.",
BANISH_RUNES = "This devil will resurrect after death, unless its remains are within a rune.",
BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
BANISH_DESTRUCTION = "Its corpse must be utterly destroyed to prevent resurrection.",
BANISH_FUNERAL_GARB = "If clad in funeral garments, this devil will be unable to resurrect. Should the clothes not fit, lay them gently on top of the devil's corpse."
),
LAW = list(
OBLIGATION_FOOD = "When not acting in self defense, you must always offer your victim food before harming them.",
OBLIGATION_FIDDLE = "When not in immediate danger, if you are challenged to a musical duel, you must accept it. You are not obligated to duel the same person twice.",
OBLIGATION_DANCEOFF = "When not in immediate danger, if you are challenged to a dance off, you must accept it. You are not obligated to face off with the same person twice.",
OBLIGATION_GREET = "You must always greet other people by their last name before talking with them.",
OBLIGATION_PRESENCEKNOWN = "You must always make your presence known before attacking.",
OBLIGATION_SAYNAME = "You must always say your true name after you kill someone.",
OBLIGATION_ANNOUNCEKILL = "Upon killing someone, you must make your deed known to all within earshot, over comms if reasonably possible.",
OBLIGATION_ANSWERTONAME = "If you are not under attack, you must always respond to your true name.",
BAN_HURTWOMAN = "You must never harm a female outside of self defense.",
BAN_CHAPEL = "You must never attempt to enter the chapel.",
BAN_HURTPRIEST = "You must never attack a priest.",
BAN_AVOIDWATER = "You must never willingly touch a wet surface.",
BAN_STRIKEUNCONSCIOUS = "You must never strike an unconscious person.",
BAN_HURTLIZARD = "You must never harm a lizardman outside of self defense.",
BAN_HURTANIMAL = "You must never harm a non-sentient creature or robot outside of self defense.",
BANE_SILVER = "Silver, in all of its forms shall be your downfall.",
BANE_SALT = "Salt will disrupt your magical abilities.",
BANE_LIGHT = "Blinding lights will prevent you from using offensive powers for a time.",
BANE_IRON = "Cold wrought iron shall act as poison to you.",
BANE_WHITECLOTHES = "Those clad in pristine white garments will strike you true.",
BANE_HARVEST = "The fruits of the harvest shall be your downfall.",
BANE_TOOLBOX = "Toolboxes are bad news for you, for some reason.",
BANISH_WATER = "If your corpse is filled with holy water, you will be unable to resurrect.",
BANISH_COFFIN = "If your corpse is in a coffin, you will be unable to resurrect.",
BANISH_FORMALDYHIDE = "If your corpse is embalmed, you will be unable to resurrect.",
BANISH_RUNES = "If your corpse is placed within a rune, you will be unable to resurrect.",
BANISH_CANDLES = "If your corpse is near lit candles, you will be unable to resurrect.",
BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.",
BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect."
)
))
//These are also used in the codex gigas, so let's declare them globally.
GLOBAL_LIST_INIT(devil_pre_title, list("Dark ", "Hellish ", "Fallen ", "Fiery ", "Sinful ", "Blood ", "Fluffy "))
GLOBAL_LIST_INIT(devil_title, list("Lord ", "Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept "))
GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon", "mya", "folth", "wren", "geyr", "hil", "niet", "twou", "phi", "coa"))
GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr."))
/datum/antagonist/devil
name = "Devil"
roundend_category = "devils"
antagpanel_category = "Devil"
job_rank = ROLE_DEVIL
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
var/obligation
var/ban
var/bane
var/banish
var/truename
var/list/datum/mind/soulsOwned = new
var/reviveNumber = 0
var/form = BASIC_DEVIL
var/static/list/devil_spells = typecacheof(list(
/obj/effect/proc_holder/spell/aimed/fireball/hellish,
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork,
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater,
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended,
/obj/effect/proc_holder/spell/targeted/infernal_jaunt,
/obj/effect/proc_holder/spell/targeted/sintouch,
/obj/effect/proc_holder/spell/targeted/sintouch/ascended,
/obj/effect/proc_holder/spell/targeted/summon_contract,
/obj/effect/proc_holder/spell/targeted/conjure_item/violin,
/obj/effect/proc_holder/spell/targeted/summon_dancefloor))
var/ascendable = FALSE
/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
. = ..()
return . && (ishuman(new_owner.current) || iscyborg(new_owner.current))
/datum/antagonist/devil/get_admin_commands()
. = ..()
.["Toggle ascendable"] = CALLBACK(src,.proc/admin_toggle_ascendable)
/datum/antagonist/devil/proc/admin_toggle_ascendable(mob/admin)
ascendable = !ascendable
message_admins("[key_name_admin(admin)] set [owner.current] devil ascendable to [ascendable]")
log_admin("[key_name_admin(admin)] set [owner.current] devil ascendable to [ascendable])")
/datum/antagonist/devil/admin_add(datum/mind/new_owner,mob/admin)
switch(alert(admin,"Should the devil be able to ascend",,"Yes","No","Cancel"))
if("Yes")
ascendable = TRUE
if("No")
ascendable = FALSE
else
return
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has devil'ed [new_owner.current]. [ascendable ? "(Ascendable)":""]")
log_admin("[key_name(admin)] has devil'ed [new_owner.current]. [ascendable ? "(Ascendable)":""]")
/datum/antagonist/devil/antag_listing_name()
return ..() + "([truename])"
/proc/devilInfo(name)
if(GLOB.allDevils[lowertext(name)])
return GLOB.allDevils[lowertext(name)]
else
var/datum/fakeDevil/devil = new /datum/fakeDevil(name)
GLOB.allDevils[lowertext(name)] = devil
return devil
/proc/randomDevilName()
var/name = ""
if(prob(65))
if(prob(35))
name = pick(GLOB.devil_pre_title)
name += pick(GLOB.devil_title)
var/probability = 100
name += pick(GLOB.devil_syllable)
while(prob(probability))
name += pick(GLOB.devil_syllable)
probability -= 20
if(prob(40))
name += pick(GLOB.devil_suffix)
return name
/proc/randomdevilobligation()
return pick(OBLIGATION_FOOD, OBLIGATION_FIDDLE, OBLIGATION_DANCEOFF, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME)
/proc/randomdevilban()
return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONSCIOUS, BAN_HURTLIZARD, BAN_HURTANIMAL)
/proc/randomdevilbane()
return pick(BANE_SALT, BANE_LIGHT, BANE_IRON, BANE_WHITECLOTHES, BANE_SILVER, BANE_HARVEST, BANE_TOOLBOX)
/proc/randomdevilbanish()
return pick(BANISH_WATER, BANISH_COFFIN, BANISH_FORMALDYHIDE, BANISH_RUNES, BANISH_CANDLES, BANISH_DESTRUCTION, BANISH_FUNERAL_GARB)
/datum/antagonist/devil/proc/add_soul(datum/mind/soul)
if(soulsOwned.Find(soul))
return
soulsOwned += soul
owner.current.nutrition = NUTRITION_LEVEL_FULL
to_chat(owner.current, "<span class='warning'>You feel satiated as you received a new soul.</span>")
update_hud()
switch(SOULVALUE)
if(0)
to_chat(owner.current, "<span class='warning'>Your hellish powers have been restored.</span>")
give_appropriate_spells()
if(BLOOD_THRESHOLD)
increase_blood_lizard()
if(TRUE_THRESHOLD)
increase_true_devil()
if(ARCH_THRESHOLD)
increase_arch_devil()
/datum/antagonist/devil/proc/remove_soul(datum/mind/soul)
if(soulsOwned.Remove(soul))
check_regression()
to_chat(owner.current, "<span class='warning'>You feel as though a soul has slipped from your grasp.</span>")
update_hud()
/datum/antagonist/devil/proc/check_regression()
if(form == ARCH_DEVIL)
return //arch devil can't regress
//Yes, fallthrough behavior is intended, so I can't use a switch statement.
if(form == TRUE_DEVIL && SOULVALUE < TRUE_THRESHOLD)
regress_blood_lizard()
if(form == BLOOD_LIZARD && SOULVALUE < BLOOD_THRESHOLD)
regress_humanoid()
if(SOULVALUE < 0)
give_appropriate_spells()
to_chat(owner.current, "<span class='warning'>As punishment for your failures, all of your powers except contract creation have been revoked.</span>")
/datum/antagonist/devil/proc/regress_humanoid()
to_chat(owner.current, "<span class='warning'>Your powers weaken, have more contracts be signed to regain power.</span>")
if(ishuman(owner.current))
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/human, 1)
H.regenerate_icons()
give_appropriate_spells()
if(istype(owner.current.loc, /obj/effect/dummy/phased_mob/slaughter/))
owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted.
form = BASIC_DEVIL
/datum/antagonist/devil/proc/regress_blood_lizard()
var/mob/living/carbon/true_devil/D = owner.current
to_chat(D, "<span class='warning'>Your powers weaken, have more contracts be signed to regain power.</span>")
D.oldform.forceMove(D.drop_location())
owner.transfer_to(D.oldform)
give_appropriate_spells()
qdel(D)
form = BLOOD_LIZARD
update_hud()
/datum/antagonist/devil/proc/increase_blood_lizard()
to_chat(owner.current, "<span class='warning'>You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.</span>")
sleep(50)
if(ishuman(owner.current))
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/lizard, 1)
H.underwear = "Nude"
H.undershirt = "Nude"
H.socks = "Nude"
H.dna.features["mcolor"] = "511" //A deep red
H.regenerate_icons()
else //Did the devil get hit by a staff of transmutation?
owner.current.color = "#501010"
give_appropriate_spells()
form = BLOOD_LIZARD
/datum/antagonist/devil/proc/increase_true_devil()
to_chat(owner.current, "<span class='warning'>You feel as though your current form is about to shed. You will soon turn into a true devil.</span>")
sleep(50)
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
A.faction |= "hell"
owner.current.forceMove(A)
A.oldform = owner.current
owner.transfer_to(A)
A.set_name()
give_appropriate_spells()
form = TRUE_DEVIL
update_hud()
/datum/antagonist/devil/proc/increase_arch_devil()
if(!ascendable)
return
var/mob/living/carbon/true_devil/D = owner.current
to_chat(D, "<span class='warning'>You feel as though your form is about to ascend.</span>")
sleep(50)
if(!D)
return
D.visible_message("<span class='warning'>[D]'s skin begins to erupt with spikes.</span>", \
"<span class='warning'>Your flesh begins creating a shield around yourself.</span>")
sleep(100)
if(!D)
return
D.visible_message("<span class='warning'>The horns on [D]'s head slowly grow and elongate.</span>", \
"<span class='warning'>Your body continues to mutate. Your telepathic abilities grow.</span>")
sleep(90)
if(!D)
return
D.visible_message("<span class='warning'>[D]'s body begins to violently stretch and contort.</span>", \
"<span class='warning'>You begin to rend apart the final barriers to ultimate power.</span>")
sleep(40)
if(!D)
return
to_chat(D, "<i><b>Yes!</b></i>")
sleep(10)
if(!D)
return
to_chat(D, "<i><b><span class='big'>YES!!</span></b></i>")
sleep(10)
if(!D)
return
to_chat(D, "<i><b><span class='reallybig'>YE--</span></b></i>")
sleep(1)
if(!D)
return
send_to_playing_players("<font size=5><span class='danger'><b>\"SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!\"</font></span>")
sound_to_playing_players('sound/hallucinations/veryfar_noise.ogg')
give_appropriate_spells()
D.convert_to_archdevil()
if(istype(D.loc, /obj/effect/dummy/phased_mob/slaughter/))
D.forceMove(get_turf(D))//Fixes dying while jaunted leaving you permajaunted.
var/area/A = get_area(owner.current)
if(A)
notify_ghosts("An arch devil has ascended in \the [A.name]. Reach out to the devil to be given a new shell for your soul.", source = owner.current, action=NOTIFY_ATTACK)
sleep(50)
if(!SSticker.mode.devil_ascended)
SSshuttle.emergency.request(null, set_coefficient = 0.3)
SSticker.mode.devil_ascended++
form = ARCH_DEVIL
/datum/antagonist/devil/proc/remove_spells()
for(var/X in owner.spell_list)
var/obj/effect/proc_holder/spell/S = X
if(is_type_in_typecache(S, devil_spells))
owner.RemoveSpell(S)
/datum/antagonist/devil/proc/give_summon_contract()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null))
if(obligation == OBLIGATION_FIDDLE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/violin(null))
else if(obligation == OBLIGATION_DANCEOFF)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_dancefloor(null))
/datum/antagonist/devil/proc/give_appropriate_spells()
remove_spells()
give_summon_contract()
if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
give_arch_spells()
else if(SOULVALUE >= TRUE_THRESHOLD)
give_true_spells()
else if(SOULVALUE >= BLOOD_THRESHOLD)
give_blood_spells()
else if(SOULVALUE >= 0)
give_base_spells()
/datum/antagonist/devil/proc/give_base_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null))
/datum/antagonist/devil/proc/give_blood_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
/datum/antagonist/devil/proc/give_true_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null))
/datum/antagonist/devil/proc/give_arch_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch/ascended(null))
/datum/antagonist/devil/proc/beginResurrectionCheck(mob/living/body)
if(SOULVALUE>0)
to_chat(owner.current, "<span class='userdanger'>Your body has been damaged to the point that you may no longer use it. At the cost of some of your power, you will return to life soon. Remain in your body.</span>")
sleep(DEVILRESURRECTTIME)
if (!body || body.stat == DEAD)
if(SOULVALUE>0)
if(check_banishment(body))
to_chat(owner.current, "<span class='userdanger'>Unfortunately, the mortals have finished a ritual that prevents your resurrection.</span>")
return -1
else
to_chat(owner.current, "<span class='userdanger'>WE LIVE AGAIN!</span>")
return hellish_resurrection(body)
else
to_chat(owner.current, "<span class='userdanger'>Unfortunately, the power that stemmed from your contracts has been extinguished. You no longer have enough power to resurrect.</span>")
return -1
else
to_chat(owner.current, "<span class='danger'> You seem to have resurrected without your hellish powers.</span>")
else
to_chat(owner.current, "<span class='userdanger'>Your hellish powers are too weak to resurrect yourself.</span>")
/datum/antagonist/devil/proc/check_banishment(mob/living/body)
switch(banish)
if(BANISH_WATER)
if(iscarbon(body))
var/mob/living/carbon/H = body
return H.reagents.has_reagent("holy water")
return 0
if(BANISH_COFFIN)
return (body && istype(body.loc, /obj/structure/closet/crate/coffin))
if(BANISH_FORMALDYHIDE)
if(iscarbon(body))
var/mob/living/carbon/H = body
return H.reagents.has_reagent("formaldehyde")
return 0
if(BANISH_RUNES)
if(body)
for(var/obj/effect/decal/cleanable/crayon/R in range(0,body))
if (R.name == "rune")
return 1
return 0
if(BANISH_CANDLES)
if(body)
var/count = 0
for(var/obj/item/candle/C in range(1,body))
count += C.lit
if(count>=4)
return 1
return 0
if(BANISH_DESTRUCTION)
if(body)
return 0
return 1
if(BANISH_FUNERAL_GARB)
if(ishuman(body))
var/mob/living/carbon/human/H = body
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/burial))
return 1
return 0
else
for(var/obj/item/clothing/under/burial/B in range(0,body))
if(B.loc == get_turf(B)) //Make sure it's not in someone's inventory or something.
return 1
return 0
/datum/antagonist/devil/proc/hellish_resurrection(mob/living/body)
message_admins("[owner.name] (true name is: [truename]) is resurrecting using hellish energy.</a>")
if(SOULVALUE < ARCH_THRESHOLD || !ascendable) // once ascended, arch devils do not go down in power by any means.
reviveNumber += LOSS_PER_DEATH
update_hud()
if(body)
body.revive(TRUE, TRUE) //Adminrevive also recovers organs, preventing someone from resurrecting without a heart.
if(istype(body.loc, /obj/effect/dummy/phased_mob/slaughter/))
body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted.
if(istype(body, /mob/living/carbon/true_devil))
var/mob/living/carbon/true_devil/D = body
if(D.oldform)
D.oldform.revive(1,0) // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body.
if(body.stat == DEAD)
create_new_body()
else
create_new_body()
check_regression()
/datum/antagonist/devil/proc/create_new_body()
if(GLOB.blobstart.len > 0)
var/turf/targetturf = get_turf(pick(GLOB.blobstart))
var/mob/currentMob = owner.current
if(!currentMob)
currentMob = owner.get_ghost()
if(!currentMob)
message_admins("[owner.name]'s devil resurrection failed due to client logoff. Aborting.")
return -1
if(currentMob.mind != owner)
message_admins("[owner.name]'s devil resurrection failed due to becoming a new mob. Aborting.")
return -1
currentMob.change_mob_type( /mob/living/carbon/human, targetturf, null, 1)
var/mob/living/carbon/human/H = owner.current
H.equip_to_slot_or_del(new /obj/item/clothing/under/lawyer/black(H), SLOT_W_UNIFORM)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), SLOT_SHOES)
H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), SLOT_HANDS)
H.equip_to_slot_or_del(new /obj/item/pen(H), SLOT_L_STORE)
if(SOULVALUE >= BLOOD_THRESHOLD)
H.set_species(/datum/species/lizard, 1)
H.underwear = "Nude"
H.undershirt = "Nude"
H.socks = "Nude"
H.dna.features["mcolor"] = "511"
H.regenerate_icons()
if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(targetturf)
A.faction |= "hell"
H.forceMove(A)
A.oldform = H
owner.transfer_to(A, TRUE)
A.set_name()
if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
A.convert_to_archdevil()
else
throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
/datum/antagonist/devil/proc/update_hud()
if(iscarbon(owner.current))
var/mob/living/C = owner.current
if(C.hud_used && C.hud_used.devilsouldisplay)
C.hud_used.devilsouldisplay.update_counter(SOULVALUE)
/datum/antagonist/devil/greet()
to_chat(owner.current, "<span class='warning'><b>You remember your link to the infernal. You are [truename], an agent of hell, a devil. And you were sent to the plane of creation for a reason. A greater purpose. Convince the crew to sin, and embroiden Hell's grasp.</b></span>")
to_chat(owner.current, "<span class='warning'><b>However, your infernal form is not without weaknesses.</b></span>")
to_chat(owner.current, "You may not use violence to coerce someone into selling their soul.")
to_chat(owner.current, "You may not directly and knowingly physically harm a devil, other than yourself.")
to_chat(owner.current, GLOB.lawlorify[LAW][bane])
to_chat(owner.current, GLOB.lawlorify[LAW][ban])
to_chat(owner.current, GLOB.lawlorify[LAW][obligation])
to_chat(owner.current, GLOB.lawlorify[LAW][banish])
to_chat(owner.current, "<span class='warning'>Remember, the crew can research your weaknesses if they find out your devil name.</span><br>")
.=..()
/datum/antagonist/devil/on_gain()
truename = randomDevilName()
ban = randomdevilban()
bane = randomdevilbane()
obligation = randomdevilobligation()
banish = randomdevilbanish()
GLOB.allDevils[lowertext(truename)] = src
antag_memory += "Your devilic true name is [truename]<br>[GLOB.lawlorify[LAW][ban]]<br>You may not use violence to coerce someone into selling their soul.<br>You may not directly and knowingly physically harm a devil, other than yourself.<br>[GLOB.lawlorify[LAW][bane]]<br>[GLOB.lawlorify[LAW][obligation]]<br>[GLOB.lawlorify[LAW][banish]]<br>"
if(issilicon(owner.current))
var/mob/living/silicon/robot_devil = owner.current
var/laws = list("You may not use violence to coerce someone into selling their soul.", "You may not directly and knowingly physically harm a devil, other than yourself.", GLOB.lawlorify[LAW][ban], GLOB.lawlorify[LAW][obligation], "Accomplish your objectives at all costs.")
robot_devil.set_law_sixsixsix(laws)
sleep(10)
if(owner.assigned_role == "Clown" && ishuman(owner.current))
var/mob/living/carbon/human/S = owner.current
to_chat(S, "<span class='notice'>Your infernal nature has allowed you to overcome your clownishness.</span>")
S.dna.remove_mutation(CLOWNMUT)
.=..()
/datum/antagonist/devil/on_removal()
to_chat(owner.current, "<span class='userdanger'>Your infernal link has been severed! You are no longer a devil!</span>")
.=..()
/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
give_appropriate_spells()
owner.current.grant_all_languages(TRUE)
update_hud()
.=..()
/datum/antagonist/devil/remove_innate_effects(mob/living/mob_override)
for(var/X in owner.spell_list)
var/obj/effect/proc_holder/spell/S = X
if(is_type_in_typecache(S, devil_spells))
owner.RemoveSpell(S)
.=..()
/datum/antagonist/devil/proc/printdevilinfo()
var/list/parts = list()
parts += "The devil's true name is: [truename]"
parts += "The devil's bans were:"
parts += "[FOURSPACES][GLOB.lawlorify[LORE][ban]]"
parts += "[FOURSPACES][GLOB.lawlorify[LORE][bane]]"
parts += "[FOURSPACES][GLOB.lawlorify[LORE][obligation]]"
parts += "[FOURSPACES][GLOB.lawlorify[LORE][banish]]"
return parts.Join("<br>")
/datum/antagonist/devil/roundend_report()
var/list/parts = list()
parts += printplayer(owner)
parts += printdevilinfo()
parts += printobjectives(objectives)
return parts.Join("<br>")
//A simple super light weight datum for the codex gigas.
/datum/fakeDevil
var/truename
var/bane
var/obligation
var/ban
var/banish
var/ascendable
/datum/fakeDevil/New(name = randomDevilName())
truename = name
bane = randomdevilbane()
obligation = randomdevilobligation()
ban = randomdevilban()
banish = randomdevilbanish()
ascendable = prob(25)
-74
View File
@@ -1,74 +0,0 @@
//////////////////The Monster
/mob/living/simple_animal/imp
name = "imp"
real_name = "imp"
unique_name = TRUE
desc = "A large, menacing creature covered in armored black scales."
speak_emote = list("cackles")
emote_hear = list("cackles","screeches")
response_help = "thinks better of touching"
response_disarm = "flails at"
response_harm = "punches"
icon = 'icons/mob/mob.dmi'
icon_state = "imp"
icon_living = "imp"
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
status_flags = CANPUSH
attack_sound = 'sound/magic/demon_attack1.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 250 //Weak to cold
maxbodytemp = INFINITY
faction = list("hell")
attacktext = "wildly tears into"
maxHealth = 200
health = 200
healable = 0
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
obj_damage = 40
melee_damage_lower = 10
melee_damage_upper = 15
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
var/boost = 0
bloodcrawl = BLOODCRAWL_EAT
var/list/consumed_mobs = list()
var/playstyle_string = "<span class='big bold'>You are an imp,</span><B> a mischievous creature from hell. You are the lowest rank on the hellish totem pole \
Though you are not obligated to help, perhaps by aiding a higher ranking devil, you might just get a promotion. However, you are incapable \
of intentionally harming a fellow devil.</B>"
/mob/living/simple_animal/imp/Initialize()
..()
boost = world.time + 30
/mob/living/simple_animal/imp/Life()
..()
if(boost<world.time)
speed = 1
else
speed = 0
/mob/living/simple_animal/imp/death()
..(1)
playsound(get_turf(src),'sound/magic/demon_dies.ogg', 200, 1)
visible_message("<span class='danger'>[src] screams in agony as it sublimates into a sulfurous smoke.</span>")
ghostize()
qdel(src)
/datum/antagonist/imp
name = "Imp"
antagpanel_category = "Devil"
show_in_roundend = FALSE
/datum/antagonist/imp/on_gain()
. = ..()
give_objectives()
/datum/antagonist/imp/proc/give_objectives()
var/datum/objective/newobjective = new
newobjective.explanation_text = "Try to get a promotion to a higher devilic rank."
newobjective.owner = owner
objectives += newobjective
@@ -1,220 +0,0 @@
#define DEVIL_HANDS_LAYER 1
#define DEVIL_HEAD_LAYER 2
#define DEVIL_TOTAL_LAYERS 2
/mob/living/carbon/true_devil
name = "True Devil"
desc = "A pile of infernal energy, taking a vaguely humanoid form."
icon = 'icons/mob/32x64.dmi'
icon_state = "true_devil"
gender = NEUTER
health = 350
maxHealth = 350
ventcrawler = VENTCRAWLER_NONE
density = TRUE
pass_flags = 0
sight = (SEE_TURFS | SEE_OBJS)
status_flags = CANPUSH
spacewalk = TRUE
mob_size = MOB_SIZE_LARGE
held_items = list(null, null)
bodyparts = list(/obj/item/bodypart/chest/devil, /obj/item/bodypart/head/devil, /obj/item/bodypart/l_arm/devil,
/obj/item/bodypart/r_arm/devil, /obj/item/bodypart/r_leg/devil, /obj/item/bodypart/l_leg/devil)
hud_type = /datum/hud/devil
var/ascended = FALSE
var/mob/living/oldform
var/list/devil_overlays[DEVIL_TOTAL_LAYERS]
/mob/living/carbon/true_devil/Initialize()
create_bodyparts() //initialize bodyparts
create_internal_organs()
grant_all_languages(omnitongue=TRUE)
..()
/mob/living/carbon/true_devil/create_internal_organs()
internal_organs += new /obj/item/organ/brain
internal_organs += new /obj/item/organ/tongue
internal_organs += new /obj/item/organ/eyes
internal_organs += new /obj/item/organ/ears/invincible //Prevents hearing loss from poorly aimed fireballs.
..()
/mob/living/carbon/true_devil/proc/convert_to_archdevil()
maxHealth = 500 // not an IMPOSSIBLE amount, but still near impossible.
ascended = TRUE
health = maxHealth
icon_state = "arch_devil"
/mob/living/carbon/true_devil/proc/set_name()
var/datum/antagonist/devil/devilinfo = mind.has_antag_datum(/datum/antagonist/devil)
name = devilinfo.truename
real_name = name
/mob/living/carbon/true_devil/Login()
..()
var/datum/antagonist/devil/devilinfo = mind.has_antag_datum(/datum/antagonist/devil)
devilinfo.greet()
mind.announce_objectives()
/mob/living/carbon/true_devil/death(gibbed)
stat = DEAD
..(gibbed)
drop_all_held_items()
INVOKE_ASYNC(mind.has_antag_datum(/datum/antagonist/devil), /datum/antagonist/devil/proc/beginResurrectionCheck, src)
/mob/living/carbon/true_devil/examine(mob/user)
var/msg = "<span class='info'>*---------*\nThis is [icon2html(src, user)] <b>[src]</b>!\n"
//Left hand items
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
msg += "It is holding [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))].\n"
//Braindead
if(!client && stat != DEAD)
msg += "The devil seems to be in deep contemplation.\n"
//Damaged
if(stat == DEAD)
msg += "<span class='deadsay'>The hellfire seems to have been extinguished, for now at least.</span>\n"
else if(health < (maxHealth/10))
msg += "<span class='warning'>You can see hellfire inside its gaping wounds.</span>\n"
else if(health < (maxHealth/2))
msg += "<span class='warning'>You can see hellfire inside its wounds.</span>\n"
msg += "*---------*</span>"
to_chat(user, msg)
/mob/living/carbon/true_devil/IsAdvancedToolUser()
return 1
/mob/living/carbon/true_devil/resist_buckle()
if(buckled)
buckled.user_unbuckle_mob(src,src)
visible_message("<span class='warning'>[src] easily breaks out of [p_their()] handcuffs!</span>", \
"<span class='notice'>With just a thought your handcuffs fall off.</span>")
/mob/living/carbon/true_devil/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
if(incapacitated())
to_chat(src, "<span class='warning'>You can't do that right now!</span>")
return FALSE
if(be_close && !in_range(M, src))
to_chat(src, "<span class='warning'>You are too far away!</span>")
return FALSE
return TRUE
/mob/living/carbon/true_devil/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
return 666
/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
if(mind && has_bane(BANE_LIGHT))
mind.disrupt_spells(-500)
return ..() //flashes don't stop devils UNLESS it's their bane.
/mob/living/carbon/true_devil/soundbang_act()
return 0
/mob/living/carbon/true_devil/get_ear_protection()
return 2
/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone)
var/weakness = check_weakness(I, user)
apply_damage(I.force * weakness, I.damtype, def_zone)
var/message_verb = ""
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
else if(I.force)
message_verb = "attacked"
var/attack_message = "[src] has been [message_verb] with [I]."
if(user)
user.do_attack_animation(src)
if(user in viewers(src, null))
attack_message = "[user] has [message_verb] [src] with [I]!"
if(message_verb)
visible_message("<span class='danger'>[attack_message]</span>",
"<span class='userdanger'>[attack_message]</span>", null, COMBAT_MESSAGE_RANGE)
return TRUE
/mob/living/carbon/true_devil/singularity_act()
if(ascended)
return 0
return ..()
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob)
if(ascended || user.mind.soulOwner == src.mind)
var/mob/living/simple_animal/imp/S = new(get_turf(loc))
user.transfer_ckey(S, FALSE)
var/datum/antagonist/imp/A = new()
S.mind.add_antag_datum(A)
else
return ..()
/mob/living/carbon/true_devil/can_be_revived()
return 1
/mob/living/carbon/true_devil/resist_fire()
//They're immune to fire.
/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
. = ..()
if(.)
switch(M.a_intent)
if ("harm")
var/damage = rand(1, 5)
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
"<span class='userdanger'>[M] has punched [src]!</span>")
adjustBruteLoss(damage)
log_combat(M, src, "attacked")
updatehealth()
if ("disarm")
if (!lying && !ascended) //No stealing the arch devil's pitchfork.
if (prob(5))
Unconscious(40)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
log_combat(M, src, "pushed")
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
"<span class='userdanger'>[M] has pushed down [src]!</span>")
else
if (prob(25))
dropItemToGround(get_active_held_item())
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
"<span class='userdanger'>[M] has disarmed [src]!</span>")
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] has attempted to disarm [src]!</span>")
/mob/living/carbon/true_devil/handle_breathing()
// devils do not need to breathe
/mob/living/carbon/true_devil/is_literate()
return 1
/mob/living/carbon/true_devil/ex_act(severity, ex_target)
if(!ascended)
var/b_loss
switch (severity)
if (1)
b_loss = 500
if (2)
b_loss = 150
if(3)
b_loss = 30
if(has_bane(BANE_LIGHT))
b_loss *=2
adjustBruteLoss(b_loss)
return ..()
/mob/living/carbon/true_devil/update_body() //we don't use the bodyparts layer for devils.
return
/mob/living/carbon/true_devil/update_body_parts()
return
/mob/living/carbon/true_devil/update_damage_overlays() //devils don't have damage overlays.
return
@@ -1,97 +0,0 @@
/datum/antagonist/disease
name = "Sentient Disease"
roundend_category = "diseases"
antagpanel_category = "Disease"
var/disease_name = ""
/datum/antagonist/disease/on_gain()
owner.special_role = "Sentient Disease"
owner.assigned_role = "Sentient Disease"
var/datum/objective/O = new /datum/objective/disease_infect()
O.owner = owner
objectives += O
O = new /datum/objective/disease_infect_centcom()
O.owner = owner
objectives += O
. = ..()
/datum/antagonist/disease/greet()
to_chat(owner.current, "<span class='notice'>You are the [owner.special_role]!</span>")
to_chat(owner.current, "<span class='notice'>Infect members of the crew to gain adaptation points, and spread your infection further.</span>")
owner.announce_objectives()
/datum/antagonist/disease/apply_innate_effects(mob/living/mob_override)
if(!istype(owner.current, /mob/camera/disease))
var/turf/T = get_turf(owner.current)
T = T ? T : SSmapping.get_station_center()
var/mob/camera/disease/D = new /mob/camera/disease(T)
owner.transfer_to(D)
/datum/antagonist/disease/admin_add(datum/mind/new_owner,mob/admin)
..()
var/mob/camera/disease/D = new_owner.current
D.pick_name()
/datum/antagonist/disease/roundend_report()
var/list/result = list()
result += "<b>Disease name:</b> [disease_name]"
result += printplayer(owner)
var/win = TRUE
var/objectives_text = ""
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
count++
result += objectives_text
var/special_role_text = lowertext(name)
if(win)
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
else
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
if(istype(owner.current, /mob/camera/disease))
var/mob/camera/disease/D = owner.current
result += "<B>[disease_name] completed the round with [D.hosts.len] infected hosts, and reached a maximum of [D.total_points] concurrent infections.</B>"
result += "<B>[disease_name] completed the round with the following adaptations:</B>"
var/list/adaptations = list()
for(var/V in D.purchased_abilities)
var/datum/disease_ability/A = V
adaptations += A.name
result += adaptations.Join(", ")
return result.Join("<br>")
/datum/objective/disease_infect
explanation_text = "Survive and infect as many people as possible."
/datum/objective/disease_infect/check_completion()
var/mob/camera/disease/D = owner.current
if(istype(D) && D.hosts.len) //theoretically it should not exist if it has no hosts, but better safe than sorry.
return TRUE
return FALSE
/datum/objective/disease_infect_centcom
explanation_text = "Ensure that at least one infected host escapes on the shuttle or an escape pod."
/datum/objective/disease_infect_centcom/check_completion()
var/mob/camera/disease/D = owner.current
if(!istype(D))
return FALSE
for(var/V in D.hosts)
var/mob/living/L = V
if(L.onCentCom() || L.onSyndieBase())
return TRUE
return FALSE
@@ -1,27 +0,0 @@
/datum/round_event_control/sentient_disease
name = "Spawn Sentient Disease"
typepath = /datum/round_event/ghost_role/sentient_disease
weight = 7
gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 5
/datum/round_event/ghost_role/sentient_disease
role_name = "sentient disease"
/datum/round_event/ghost_role/sentient_disease/spawn_role()
var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/observer/selected = pick_n_take(candidates)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
selected.transfer_ckey(virus, FALSE)
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by an event.")
log_game("[key_name(virus)] was spawned as a sentient disease by an event.")
spawned_mobs += virus
return SUCCESSFUL_SPAWN
@@ -1,402 +0,0 @@
/*
A mob of type /mob/camera/disease is an overmind coordinating at least one instance of /datum/disease/advance/sentient_disease
that has infected a host. All instances in a host will be synchronized with the stats of the overmind's disease_template. Any
samples outside of a host will retain the stats they had when they left the host, but infecting a new host will cause
the new instance inside the host to be updated to the template's stats.
*/
/mob/camera/disease
name = "Sentient Disease"
real_name = "Sentient Disease"
desc = ""
icon = 'icons/mob/cameramob.dmi'
icon_state = "marker"
mouse_opacity = MOUSE_OPACITY_ICON
move_on_shuttle = FALSE
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = BELOW_MOB_LAYER
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
sight = SEE_SELF|SEE_THRU
initial_language_holder = /datum/language_holder/universal
var/freemove = TRUE
var/freemove_end = 0
var/const/freemove_time = 1200
var/freemove_end_timerid
var/datum/action/innate/disease_adapt/adaptation_menu_action
var/datum/disease_ability/examining_ability
var/datum/browser/browser
var/browser_open = FALSE
var/mob/living/following_host
var/list/disease_instances
var/list/hosts //this list is associative, affected_mob -> disease_instance
var/datum/disease/advance/sentient_disease/disease_template
var/total_points = 0
var/points = 0
var/last_move_tick = 0
var/move_delay = 1
var/next_adaptation_time = 0
var/adaptation_cooldown = 600
var/list/purchased_abilities
var/list/unpurchased_abilities
/mob/camera/disease/Initialize(mapload)
.= ..()
disease_instances = list()
hosts = list()
purchased_abilities = list()
unpurchased_abilities = list()
disease_template = new /datum/disease/advance/sentient_disease()
disease_template.overmind = src
qdel(SSdisease.archive_diseases[disease_template.GetDiseaseID()])
SSdisease.archive_diseases[disease_template.GetDiseaseID()] = disease_template //important for stuff that uses disease IDs
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.add_hud_to(src)
browser = new /datum/browser(src, "disease_menu", "Adaptation Menu", 1000, 770, src)
freemove_end = world.time + freemove_time
freemove_end_timerid = addtimer(CALLBACK(src, .proc/infect_random_patient_zero), freemove_time, TIMER_STOPPABLE)
/mob/camera/disease/Destroy()
. = ..()
QDEL_NULL(adaptation_menu_action)
for(var/V in GLOB.sentient_disease_instances)
var/datum/disease/advance/sentient_disease/S = V
if(S.overmind == src)
S.overmind = null
/mob/camera/disease/Login()
..()
if(freemove)
to_chat(src, "<span class='warning'>You have [DisplayTimeText(freemove_end - world.time)] to select your first host. Click on a human to select your host.</span>")
/mob/camera/disease/Stat()
..()
if(statpanel("Status"))
if(freemove)
stat("Host Selection Time: [round((freemove_end - world.time)/10)]s")
else
stat("Adaptation Points: [points]/[total_points]")
stat("Hosts: [disease_instances.len]")
var/adapt_ready = next_adaptation_time - world.time
if(adapt_ready > 0)
stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s")
/mob/camera/disease/examine(mob/user)
..()
if(isobserver(user))
to_chat(user, "<span class='notice'>[src] has [points]/[total_points] adaptation points.</span>")
to_chat(user, "<span class='notice'>[src] has the following unlocked:</span>")
for(var/A in purchased_abilities)
var/datum/disease_ability/B = A
if(istype(B))
to_chat(user, "<span class='notice'>[B.name]</span>")
/mob/camera/disease/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
return
/mob/camera/disease/Move(NewLoc, Dir = 0)
if(freemove)
forceMove(NewLoc)
else
if(world.time > (last_move_tick + move_delay))
follow_next(Dir & NORTHWEST)
last_move_tick = world.time
/mob/camera/disease/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
var/atom/movable/virtualspeaker/V = speaker
to_follow = V.source
var/link
if(to_follow in hosts)
link = FOLLOW_LINK(src, to_follow)
else
link = ""
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
to_chat(src, "[link] [message]")
/mob/camera/disease/mind_initialize()
. = ..()
if(!mind.has_antag_datum(/datum/antagonist/disease))
mind.add_antag_datum(/datum/antagonist/disease)
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
/mob/camera/disease/proc/pick_name()
var/static/list/taken_names
if(!taken_names)
taken_names = list("Unknown" = TRUE)
for(var/T in (subtypesof(/datum/disease) - /datum/disease/advance))
var/datum/disease/D = T
taken_names[initial(D.name)] = TRUE
var/set_name
while(!set_name)
var/input = stripped_input(src, "Select a name for your disease", "Select Name", "", MAX_NAME_LEN)
if(!input)
set_name = "Sentient Virus"
break
if(taken_names[input])
to_chat(src, "<span class='notice'>You cannot use the name of such a well-known disease!</span>")
else
set_name = input
real_name = "[set_name] (Sentient Disease)"
name = "[set_name] (Sentient Disease)"
disease_template.AssignName(set_name)
var/datum/antagonist/disease/A = mind.has_antag_datum(/datum/antagonist/disease)
if(A)
A.disease_name = set_name
/mob/camera/disease/proc/infect_random_patient_zero(del_on_fail = TRUE)
if(!freemove)
return FALSE
var/list/possible_hosts = list()
var/list/afk_possible_hosts = list()
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
var/turf/T = get_turf(H)
if((H.stat != DEAD) && T && is_station_level(T.z) && H.CanContractDisease(disease_template))
if(H.client && !H.client.is_afk())
possible_hosts += H
else
afk_possible_hosts += H
shuffle_inplace(possible_hosts)
shuffle_inplace(afk_possible_hosts)
possible_hosts += afk_possible_hosts //ideally we want a not-afk person, but we will settle for an afk one if there are no others (mostly for testing)
while(possible_hosts.len)
var/mob/living/carbon/human/target = possible_hosts[1]
if(force_infect(target))
return TRUE
possible_hosts.Cut(1, 2)
if(del_on_fail)
to_chat(src, "<span class=userdanger'>No hosts were available for your disease to infect.</span>")
qdel(src)
return FALSE
/mob/camera/disease/proc/force_infect(mob/living/L)
var/datum/disease/advance/sentient_disease/V = disease_template.Copy()
var/result = L.ForceContractDisease(V, FALSE, TRUE)
if(result && freemove)
end_freemove()
return result
/mob/camera/disease/proc/end_freemove()
if(!freemove)
return
freemove = FALSE
move_on_shuttle = TRUE
adaptation_menu_action = new /datum/action/innate/disease_adapt()
adaptation_menu_action.Grant(src)
for(var/V in GLOB.disease_ability_singletons)
unpurchased_abilities[V] = TRUE
var/datum/disease_ability/A = V
if(A.start_with && A.CanBuy(src))
A.Buy(src, TRUE, FALSE)
if(freemove_end_timerid)
deltimer(freemove_end_timerid)
sight = SEE_SELF
/mob/camera/disease/proc/add_infection(datum/disease/advance/sentient_disease/V)
disease_instances += V
hosts[V.affected_mob] = V
total_points = max(total_points, disease_instances.len)
points += 1
var/image/holder = V.affected_mob.hud_list[SENTIENT_DISEASE_HUD]
var/mutable_appearance/MA = new /mutable_appearance(holder)
MA.icon_state = "virus_infected"
MA.layer = BELOW_MOB_LAYER
MA.color = COLOR_GREEN_GRAY
MA.alpha = 200
holder.appearance = MA
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.add_to_hud(V.affected_mob)
to_chat(src, "<span class='notice'>A new host, <b>[V.affected_mob.real_name]</b>, has been infected.</span>")
if(!following_host)
set_following(V.affected_mob)
refresh_adaptation_menu()
/mob/camera/disease/proc/remove_infection(datum/disease/advance/sentient_disease/V)
if(QDELETED(src))
disease_instances -= V
hosts -= V.affected_mob
else
to_chat(src, "<span class='notice'>One of your hosts, <b>[V.affected_mob.real_name]</b>, has been purged of your infection.</span>")
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.remove_from_hud(V.affected_mob)
if(following_host == V.affected_mob)
follow_next()
disease_instances -= V
hosts -= V.affected_mob
if(!disease_instances.len)
to_chat(src, "<span class='userdanger'>The last of your infection has disappeared.</span>")
set_following(null)
qdel(src)
refresh_adaptation_menu()
/mob/camera/disease/proc/set_following(mob/living/L)
if(following_host)
UnregisterSignal(following_host, COMSIG_MOVABLE_MOVED)
RegisterSignal(L, COMSIG_MOVABLE_MOVED, .proc/follow_mob)
following_host = L
follow_mob()
/mob/camera/disease/proc/follow_next(reverse = FALSE)
var/index = hosts.Find(following_host)
if(index)
if(reverse)
index = index == 1 ? hosts.len : index - 1
else
index = index == hosts.len ? 1 : index + 1
set_following(hosts[index])
/mob/camera/disease/proc/follow_mob(datum/source, newloc, dir)
var/turf/T = get_turf(following_host)
if(T)
forceMove(T)
/mob/camera/disease/DblClickOn(var/atom/A, params)
if(hosts[A])
set_following(A)
else
..()
/mob/camera/disease/ClickOn(var/atom/A, params)
if(freemove && ishuman(A))
var/mob/living/carbon/human/H = A
if(alert(src, "Select [H.name] as your initial host?", "Select Host", "Yes", "No") != "Yes")
return
if(!freemove)
return
if(QDELETED(H) || !force_infect(H))
to_chat(src, "<span class='warning'>[H ? H.name : "Host"] cannot be infected.</span>")
else
..()
/mob/camera/disease/proc/adapt_cooldown()
to_chat(src, "<span class='notice'>You have altered your genetic structure. You will be unable to adapt again for [DisplayTimeText(adaptation_cooldown)].</span>")
next_adaptation_time = world.time + adaptation_cooldown
addtimer(CALLBACK(src, .proc/notify_adapt_ready), adaptation_cooldown)
/mob/camera/disease/proc/notify_adapt_ready()
to_chat(src, "<span class='notice'>You are now ready to adapt again.</span>")
refresh_adaptation_menu()
/mob/camera/disease/proc/refresh_adaptation_menu()
if(browser_open)
adaptation_menu()
/mob/camera/disease/proc/adaptation_menu()
var/datum/disease/advance/sentient_disease/DT = disease_template
if(!DT)
return
var/list/dat = list()
if(examining_ability)
dat += "<a href='byond://?src=[REF(src)];main_menu=1'>Back</a><br><h1>[examining_ability.name]</h1>[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
else
dat += "<h1>Disease Statistics</h1><br>\
Resistance: [DT.totalResistance()]<br>\
Stealth: [DT.totalStealth()]<br>\
Stage Speed: [DT.totalStageSpeed()]<br>\
Transmissibility: [DT.totalTransmittable()]<hr>\
Cure: [DT.cure_text]"
dat += "<hr><h1>Adaptations</h1>\
Points: [points] / [total_points]\
<table border=1>\
<tr><td>Cost</td><td></td><td>Unlock</td><td width='180px'>Name</td><td>Type</td><td>Description</td></tr>"
for(var/V in GLOB.disease_ability_singletons)
var/datum/disease_ability/A = V
var/purchase_text
if(unpurchased_abilities[A])
if(A.CanBuy(src))
purchase_text = "<a href='byond://?src=[REF(src)];buy_ability=[REF(A)]'>Purchase</a>"
else
purchase_text = "<span class='linkOff'>Purchase</span>"
else
if(A.CanRefund(src))
purchase_text = "<a href='byond://?src=[REF(src)];refund_ability=[REF(A)]'>Refund</a>"
else
purchase_text = "<span class='linkOff'>Refund</span>"
dat += "<tr><td>[A.cost]</td><td>[purchase_text]</td><td>[A.required_total_points]</td><td><a href='byond://?src=[REF(src)];examine_ability=[REF(A)]'>[A.name]</a></td><td>[A.category]</td><td>[A.short_desc]</td></tr>"
dat += "</table><br>Infect many hosts at once to gain adaptation points.<hr><h1>Infected Hosts</h1>"
for(var/V in hosts)
var/mob/living/L = V
dat += "<br><a href='byond://?src=[REF(src)];follow_instance=[REF(L)]'>[L.real_name]</a>"
browser.set_content(dat.Join())
browser.open()
browser_open = TRUE
/mob/camera/disease/Topic(href, list/href_list)
..()
if(href_list["close"])
browser_open = FALSE
if(usr != src)
return
if(href_list["follow_instance"])
var/mob/living/L = locate(href_list["follow_instance"]) in hosts
set_following(L)
if(href_list["buy_ability"])
var/datum/disease_ability/A = locate(href_list["buy_ability"])
if(!istype(A))
return
if(A.CanBuy(src))
A.Buy(src)
adaptation_menu()
if(href_list["refund_ability"])
var/datum/disease_ability/A = locate(href_list["refund_ability"])
if(!istype(A))
return
if(A.CanRefund(src))
A.Refund(src)
adaptation_menu()
if(href_list["examine_ability"])
var/datum/disease_ability/A = locate(href_list["examine_ability"])
if(!istype(A))
return
examining_ability = A
adaptation_menu()
if(href_list["main_menu"])
examining_ability = null
adaptation_menu()
/datum/action/innate/disease_adapt
name = "Adaptation Menu"
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "disease_menu"
/datum/action/innate/disease_adapt/Activate()
var/mob/camera/disease/D = owner
D.adaptation_menu()
-153
View File
@@ -1,153 +0,0 @@
//Both ERT and DS are handled by the same datums since they mostly differ in equipment in objective.
/datum/team/ert
name = "Emergency Response Team"
var/datum/objective/mission //main mission
/datum/antagonist/ert
name = "Emergency Response Officer"
var/datum/team/ert/ert_team
var/leader = FALSE
var/datum/outfit/outfit = /datum/outfit/ert/security
var/role = "Security Officer"
var/list/name_source
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_PREVENT
/datum/antagonist/ert/on_gain()
update_name()
forge_objectives()
equipERT()
. = ..()
/datum/antagonist/ert/get_team()
return ert_team
/datum/antagonist/ert/New()
. = ..()
name_source = GLOB.last_names
/datum/antagonist/ert/proc/update_name()
owner.current.fully_replace_character_name(owner.current.real_name,"[role] [pick(name_source)]")
/datum/antagonist/ert/deathsquad/New()
. = ..()
name_source = GLOB.commando_names
/datum/antagonist/ert/security // kinda handled by the base template but here for completion
/datum/antagonist/ert/security/amber
outfit = /datum/outfit/ert/security/alert
/datum/antagonist/ert/security/red
outfit = /datum/outfit/ert/security/alert/red
/datum/antagonist/ert/engineer
role = "Engineer"
outfit = /datum/outfit/ert/engineer
/datum/antagonist/ert/engineer/amber
outfit = /datum/outfit/ert/engineer/alert
/datum/antagonist/ert/engineer/red
outfit = /datum/outfit/ert/engineer/alert/red
/datum/antagonist/ert/medic
role = "Medical Officer"
outfit = /datum/outfit/ert/medic
/datum/antagonist/ert/medic/amber
outfit = /datum/outfit/ert/medic/alert
/datum/antagonist/ert/medic/red
outfit = /datum/outfit/ert/medic/alert/red
/datum/antagonist/ert/commander
role = "Commander"
outfit = /datum/outfit/ert/commander
/datum/antagonist/ert/commander/amber
outfit = /datum/outfit/ert/commander/alert
/datum/antagonist/ert/commander/red
outfit = /datum/outfit/ert/commander/alert/red
/datum/antagonist/ert/deathsquad
name = "Deathsquad Trooper"
outfit = /datum/outfit/death_commando
role = "Trooper"
/datum/antagonist/ert/medic/inquisitor
outfit = /datum/outfit/ert/medic/inquisitor
/datum/antagonist/ert/security/inquisitor
outfit = /datum/outfit/ert/security/inquisitor
/datum/antagonist/ert/chaplain
role = "Chaplain"
outfit = /datum/outfit/ert/chaplain
/datum/antagonist/ert/chaplain/inquisitor
outfit = /datum/outfit/ert/chaplain/inquisitor
/datum/antagonist/ert/chaplain/on_gain()
. = ..()
owner.isholy = TRUE
/datum/antagonist/ert/commander/inquisitor
outfit = /datum/outfit/ert/commander/inquisitor
/datum/antagonist/ert/commander/inquisitor/on_gain()
. = ..()
owner.isholy = TRUE
/datum/antagonist/ert/deathsquad/leader
name = "Deathsquad Officer"
outfit = /datum/outfit/death_commando
role = "Officer"
/datum/antagonist/ert/create_team(datum/team/ert/new_team)
if(istype(new_team))
ert_team = new_team
/datum/antagonist/ert/proc/forge_objectives()
if(ert_team)
objectives |= ert_team.objectives
/datum/antagonist/ert/proc/equipERT()
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
H.equipOutfit(outfit)
/datum/antagonist/ert/greet()
if(!ert_team)
return
to_chat(owner, "<B><font size=3 color=red>You are the [name].</font></B>")
var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division."
if(leader) //If Squad Leader
missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready."
else
missiondesc += " Follow orders given to you by your squad leader."
missiondesc += "Avoid civilian casualites when possible."
missiondesc += "<BR><B>Your Mission</B> : [ert_team.mission.explanation_text]"
to_chat(owner,missiondesc)
/datum/antagonist/ert/deathsquad/greet()
if(!ert_team)
return
to_chat(owner, "<B><font size=3 color=red>You are the [name].</font></B>")
var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division."
if(leader) //If Squad Leader
missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready."
else
missiondesc += " Follow orders given to you by your squad leader."
missiondesc += "<BR><B>Your Mission</B> : [ert_team.mission.explanation_text]"
to_chat(owner,missiondesc)
@@ -1,14 +0,0 @@
/datum/antagonist/greentext
name = "winner"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE //Not that it will be there for long
/datum/antagonist/greentext/proc/forge_objectives()
var/datum/objective/O = new /datum/objective("Succeed")
O.completed = TRUE //YES!
O.owner = owner
objectives += O
/datum/antagonist/greentext/on_gain()
forge_objectives()
. = ..()
@@ -1,76 +0,0 @@
/datum/antagonist/highlander
name = "highlander"
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
ADD_TRAIT(L, TRAIT_NOGUNS, "highlander")
/datum/antagonist/highlander/remove_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
REMOVE_TRAIT(L, TRAIT_NOGUNS, "highlander")
/datum/antagonist/highlander/proc/forge_objectives()
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.set_target(new /datum/objective_item/steal/nukedisc)
objectives += steal_objective
var/datum/objective/hijack/hijack_objective = new
hijack_objective.explanation_text = "Escape on the shuttle alone. Ensure that nobody else makes it out."
hijack_objective.owner = owner
objectives += hijack_objective
/datum/antagonist/highlander/on_gain()
forge_objectives()
owner.special_role = "highlander"
give_equipment()
. = ..()
/datum/antagonist/highlander/greet()
to_chat(owner, "<span class='boldannounce'>Your [sword.name] cries out for blood. Claim the lives of others, and your own will be restored!\n\
Activate it in your hand, and it will lead to the nearest target. Attack the nuclear authentication disk with it, and you will store it.</span>")
owner.announce_objectives()
/datum/antagonist/highlander/proc/give_equipment()
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
for(var/obj/item/I in H.get_equipped_items(TRUE))
qdel(I)
for(var/obj/item/I in H.held_items)
qdel(I)
H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt/highlander(H), SLOT_W_UNIFORM)
H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), SLOT_EARS)
H.equip_to_slot_or_del(new /obj/item/clothing/head/beret/highlander(H), SLOT_HEAD)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), SLOT_SHOES)
H.equip_to_slot_or_del(new /obj/item/pinpointer/nuke(H), SLOT_L_STORE)
for(var/obj/item/pinpointer/nuke/P in H)
P.attack_self(H)
var/obj/item/card/id/W = new(H)
W.icon_state = "centcom"
W.access = get_all_accesses()
W.access += get_all_centcom_access()
W.assignment = "Highlander"
W.registered_name = H.real_name
ADD_TRAIT(W, TRAIT_NODROP, HIGHLANDER)
W.update_label(H.real_name)
H.equip_to_slot_or_del(W, SLOT_WEAR_ID)
sword = new(H)
if(!GLOB.highlander)
sword.flags_1 |= ADMIN_SPAWNED_1 //To prevent announcing
sword.pickup(H) //For the stun shielding
H.put_in_hands(sword)
var/obj/item/bloodcrawl/antiwelder = new(H)
antiwelder.name = "compulsion of honor"
antiwelder.desc = "You are unable to hold anything in this hand until you're the last one left!"
antiwelder.icon_state = "bloodhand_right"
H.put_in_hands(antiwelder)
-212
View File
@@ -1,212 +0,0 @@
#define MONKEYS_ESCAPED 1
#define MONKEYS_LIVED 2
#define MONKEYS_DIED 3
#define DISEASE_LIVED 4
/datum/antagonist/monkey
name = "Monkey"
job_rank = ROLE_MONKEY
roundend_category = "monkeys"
antagpanel_category = "Monkey"
var/datum/team/monkey/monkey_team
var/monkey_only = TRUE
/datum/antagonist/monkey/can_be_owned(datum/mind/new_owner)
return ..() && (!monkey_only || ismonkey(new_owner.current))
/datum/antagonist/monkey/get_team()
return monkey_team
/datum/antagonist/monkey/on_gain()
. = ..()
SSticker.mode.ape_infectees += owner
owner.special_role = "Infected Monkey"
var/datum/disease/D = new /datum/disease/transformation/jungle_fever/monkeymode
if(!owner.current.HasDisease(D))
owner.current.ForceContractDisease(D)
else
QDEL_NULL(D)
/datum/antagonist/monkey/greet()
to_chat(owner, "<b>You are a monkey now!</b>")
to_chat(owner, "<b>Bite humans to infect them, follow the orders of the monkey leaders, and help fellow monkeys!</b>")
to_chat(owner, "<b>Ensure at least one infected monkey escapes on the Emergency Shuttle!</b>")
to_chat(owner, "<b><i>As an intelligent monkey, you know how to use technology and how to ventcrawl while wearing things.</i></b>")
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
/datum/antagonist/monkey/on_removal()
owner.special_role = null
SSticker.mode.ape_infectees -= owner
var/datum/disease/transformation/jungle_fever/D = locate() in owner.current.diseases
if(D)
qdel(D)
. = ..()
/datum/antagonist/monkey/create_team(datum/team/monkey/new_team)
if(!new_team)
for(var/datum/antagonist/monkey/H in GLOB.antagonists)
if(!H.owner)
continue
if(H.monkey_team)
monkey_team = H.monkey_team
return
monkey_team = new /datum/team/monkey
monkey_team.update_objectives()
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
monkey_team = new_team
/datum/antagonist/monkey/proc/forge_objectives()
objectives |= monkey_team.objectives
/datum/antagonist/monkey/admin_remove(mob/admin)
var/mob/living/carbon/monkey/M = owner.current
if(istype(M))
switch(alert(admin, "Humanize?", "Humanize", "Yes", "No"))
if("Yes")
if(admin == M)
admin = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
else
M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
if("No")
//nothing
else
return
. = ..()
/datum/antagonist/monkey/leader
name = "Monkey Leader"
monkey_only = FALSE
/datum/antagonist/monkey/leader/admin_add(datum/mind/new_owner,mob/admin)
var/mob/living/carbon/human/H = new_owner.current
if(istype(H))
switch(alert(admin, "Monkeyize?", "Monkeyize", "Yes", "No"))
if("Yes")
if(admin == H)
admin = H.monkeyize()
else
H.monkeyize()
if("No")
//nothing
else
return
new_owner.add_antag_datum(src)
log_admin("[key_name(admin)] made [key_name(new_owner.current)] a monkey leader!")
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner.current)] a monkey leader!")
/datum/antagonist/monkey/leader/on_gain()
. = ..()
var/obj/item/organ/heart/freedom/F = new
F.Insert(owner.current, drop_if_replaced = FALSE)
SSticker.mode.ape_leaders += owner
owner.special_role = "Monkey Leader"
/datum/antagonist/monkey/leader/on_removal()
SSticker.mode.ape_leaders -= owner
var/obj/item/organ/heart/H = new
H.Insert(owner.current, drop_if_replaced = FALSE) //replace freedom heart with normal heart
. = ..()
/datum/antagonist/monkey/leader/greet()
to_chat(owner, "<B><span class='notice'>You are the Jungle Fever patient zero!!</B></span>")
to_chat(owner, "<b>You have been planted onto this station by the Animal Rights Consortium.</b>")
to_chat(owner, "<b>Soon the disease will transform you into an ape. Afterwards, you will be able spread the infection to others with a bite.</b>")
to_chat(owner, "<b>While your infection strain is undetectable by scanners, any other infectees will show up on medical equipment.</b>")
to_chat(owner, "<b>Your mission will be deemed a success if any of the live infected monkeys reach CentCom.</b>")
to_chat(owner, "<b>As an initial infectee, you will be considered a 'leader' by your fellow monkeys.</b>")
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
/datum/objective/monkey
explanation_text = "Ensure that infected monkeys escape on the emergency shuttle!"
martyr_compatible = TRUE
var/monkeys_to_win = 1
var/escaped_monkeys = 0
/datum/objective/monkey/check_completion()
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
if (M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
escaped_monkeys++
if(escaped_monkeys >= monkeys_to_win)
return TRUE
return FALSE
/datum/team/monkey
name = "Monkeys"
/datum/team/monkey/proc/update_objectives()
objectives = list()
var/datum/objective/monkey/O = new()
O.team = src
objectives += O
/datum/team/monkey/proc/infected_monkeys_alive()
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
if(M.HasDisease(D))
return TRUE
return FALSE
/datum/team/monkey/proc/infected_monkeys_escaped()
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
return TRUE
return FALSE
/datum/team/monkey/proc/infected_humans_escaped()
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
return TRUE
return FALSE
/datum/team/monkey/proc/infected_humans_alive()
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
if(M.HasDisease(D))
return TRUE
return FALSE
/datum/team/monkey/proc/get_result()
if(infected_monkeys_escaped())
return MONKEYS_ESCAPED
if(infected_monkeys_alive())
return MONKEYS_LIVED
if(infected_humans_alive() || infected_humans_escaped())
return DISEASE_LIVED
return MONKEYS_DIED
/datum/team/monkey/roundend_report()
var/list/parts = list()
switch(get_result())
if(MONKEYS_ESCAPED)
parts += "<span class='greentext big'><B>Monkey Major Victory!</B></span>"
parts += "<span class='greentext'><B>Central Command and [station_name()] were taken over by the monkeys! Ook ook!</B></span>"
if(MONKEYS_LIVED)
parts += "<FONT size = 3><B>Monkey Minor Victory!</B></FONT>"
parts += "<span class='greentext'><B>[station_name()] was taken over by the monkeys! Ook ook!</B></span>"
if(DISEASE_LIVED)
parts += "<span class='redtext big'><B>Monkey Minor Defeat!</B></span>"
parts += "<span class='redtext'><B>All the monkeys died, but the disease lives on! The future is uncertain.</B></span>"
if(MONKEYS_DIED)
parts += "<span class='redtext big'><B>Monkey Major Defeat!</B></span>"
parts += "<span class='redtext'><B>All the monkeys died, and Jungle Fever was wiped out!</B></span>"
var/list/leaders = get_antag_minds(/datum/antagonist/monkey/leader, TRUE)
var/list/monkeys = get_antag_minds(/datum/antagonist/monkey, TRUE)
if(LAZYLEN(leaders))
parts += "<span class='header'>The monkey leaders were:</span>"
parts += printplayerlist(SSticker.mode.ape_leaders)
if(LAZYLEN(monkeys))
parts += "<span class='header'>The monkeys were:</span>"
parts += printplayerlist(SSticker.mode.ape_infectees)
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
-160
View File
@@ -1,160 +0,0 @@
/datum/antagonist/ninja
name = "Ninja"
antagpanel_category = "Ninja"
job_rank = ROLE_NINJA
show_name_in_check_antagonists = TRUE
antag_moodlet = /datum/mood_event/focused
var/helping_station = FALSE
var/give_objectives = TRUE
var/give_equipment = TRUE
/datum/antagonist/ninja/New()
if(helping_station)
can_hijack = HIJACK_PREVENT
. = ..()
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
/datum/antagonist/ninja/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_removed(M)
/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/H = owner.current)
return H.equipOutfit(/datum/outfit/ninja)
/datum/antagonist/ninja/proc/addMemories()
antag_memory += "I am an elite mercenary assassin of the mighty Spider Clan. A <font color='red'><B>SPACE NINJA</B></font>!<br>"
antag_memory += "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by clicking the initialize UI button, to use abilities like stealth)!<br>"
antag_memory += "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.<br>"
/datum/antagonist/ninja/proc/addObjectives(quantity = 6)
var/list/possible_targets = list()
for(var/datum/mind/M in SSticker.minds)
if(M.current && M.current.stat != DEAD)
if(ishuman(M.current))
if(M.special_role)
possible_targets[M] = 0 //bad-guy
else if(M.assigned_role in GLOB.command_positions)
possible_targets[M] = 1 //good-guy
var/list/possible_objectives = list(1,2,3,4)
while(objectives.len < quantity)
switch(pick_n_take(possible_objectives))
if(1) //research
var/datum/objective/download/O = new /datum/objective/download()
O.owner = owner
O.gen_amount_goal()
objectives += O
if(2) //steal
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
O.owner = owner
objectives += O
if(3) //protect/kill
if(!possible_targets.len) continue
var/index = rand(1,possible_targets.len)
var/datum/mind/M = possible_targets[index]
var/is_bad_guy = possible_targets[M]
possible_targets.Cut(index,index+1)
if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/assassinate/O = new /datum/objective/assassinate()
O.owner = owner
O.target = M
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
objectives += O
else //protect
var/datum/objective/protect/O = new /datum/objective/protect()
O.owner = owner
O.target = M
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
objectives += O
if(4) //debrain/capture
if(!possible_targets.len) continue
var/selected = rand(1,possible_targets.len)
var/datum/mind/M = possible_targets[selected]
var/is_bad_guy = possible_targets[M]
possible_targets.Cut(selected,selected+1)
if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/debrain/O = new /datum/objective/debrain()
O.owner = owner
O.target = M
O.explanation_text = "Steal the brain of [M.current.real_name]."
objectives += O
else //capture
var/datum/objective/capture/O = new /datum/objective/capture()
O.owner = owner
O.gen_amount_goal()
objectives += O
else
break
var/datum/objective/O = new /datum/objective/survive()
O.owner = owner
objectives += O
/proc/remove_ninja(mob/living/L)
if(!L || !L.mind)
return FALSE
var/datum/antagonist/datum = L.mind.has_antag_datum(/datum/antagonist/ninja)
datum.on_removal()
return TRUE
/proc/is_ninja(mob/living/M)
return M && M.mind && M.mind.has_antag_datum(/datum/antagonist/ninja)
/datum/antagonist/ninja/greet()
SEND_SOUND(owner.current, sound('sound/effects/ninja_greeting.ogg'))
to_chat(owner.current, "I am an elite mercenary assassin of the mighty Spider Clan. A <font color='red'><B>SPACE NINJA</B></font>!")
to_chat(owner.current, "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!")
to_chat(owner.current, "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.")
owner.announce_objectives()
return
/datum/antagonist/ninja/on_gain()
if(give_objectives)
addObjectives()
addMemories()
if(give_equipment)
equip_space_ninja(owner.current)
. = ..()
/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
var/adj
switch(input("What kind of ninja?", "Ninja") as null|anything in list("Random","Syndicate","Nanotrasen","No objectives"))
if("Random")
helping_station = pick(TRUE,FALSE)
adj = ""
if("Syndicate")
helping_station = FALSE
adj = "syndie"
if("Nanotrasen")
helping_station = TRUE
adj = "friendly"
if("No objectives")
give_objectives = FALSE
adj = "objectiveless"
else
return
if(helping_station)
can_hijack = HIJACK_PREVENT
new_owner.assigned_role = ROLE_NINJA
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has [adj] ninja'ed [new_owner.current].")
log_admin("[key_name(admin)] has [adj] ninja'ed [new_owner.current].")
/datum/antagonist/ninja/proc/update_ninja_icons_added(var/mob/living/carbon/human/ninja)
var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
ninjahud.join_hud(ninja)
set_antag_hud(ninja, "ninja")
/datum/antagonist/ninja/proc/update_ninja_icons_removed(var/mob/living/carbon/human/ninja)
var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
ninjahud.leave_hud(ninja)
set_antag_hud(ninja, null)
@@ -1,119 +0,0 @@
#define CHALLENGE_TELECRYSTALS 280
#define PLAYER_SCALING 1.5
#define CHALLENGE_TIME_LIMIT 3000
#define CHALLENGE_PLAYERS_TARGET 50 //target players population. anything below is a malus to the challenge tc bonus.
#define TELECRYSTALS_MALUS_SCALING 1 //the higher the value, the bigger the malus.
#define CHALLENGE_SHUTTLE_DELAY 15000 // 25 minutes, so the ops have at least 5 minutes before the shuttle is callable.
GLOBAL_LIST_EMPTY(jam_on_wardec)
GLOBAL_VAR_INIT(war_declared, FALSE)
/obj/item/nuclear_challenge
name = "Declaration of War (Challenge Mode)"
icon = 'icons/obj/device.dmi'
icon_state = "gangtool-red"
item_state = "radio"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
desc = "Use to send a declaration of hostilities to the target, delaying your shuttle departure for 20 minutes while they prepare for your assault. \
Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. \
Must be used within five minutes, or your benefactors will lose interest."
var/declaring_war = FALSE
var/uplink_type = /obj/item/uplink/nuclear
/obj/item/nuclear_challenge/attack_self(mob/living/user)
if(!check_allowed(user))
return
declaring_war = TRUE
var/are_you_sure = alert(user, "Consult your team carefully before you declare war on [station_name()]]. Are you sure you want to alert the enemy crew? You have [DisplayTimeText(world.time-SSticker.round_start_time - CHALLENGE_TIME_LIMIT)] to decide", "Declare war?", "Yes", "No")
declaring_war = FALSE
if(!check_allowed(user))
return
if(are_you_sure == "No")
to_chat(user, "On second thought, the element of surprise isn't so bad after all.")
return
var/war_declaration = "[user.real_name] has declared [user.p_their()] intent to utterly destroy [station_name()] with a nuclear device, and dares the crew to try and stop [user.p_them()]."
declaring_war = TRUE
var/custom_threat = alert(user, "Do you want to customize your declaration?", "Customize?", "Yes", "No")
declaring_war = FALSE
if(!check_allowed(user))
return
if(custom_threat == "Yes")
declaring_war = TRUE
war_declaration = stripped_input(user, "Insert your custom declaration", "Declaration")
declaring_war = FALSE
if(!check_allowed(user) || !war_declaration)
return
priority_announce(war_declaration, title = "Declaration of War", sound = 'sound/machines/alarm.ogg')
to_chat(user, "You've attracted the attention of powerful forces within the syndicate. A bonus bundle of telecrystals has been granted to your team. Great things await you if you complete the mission.")
for(var/V in GLOB.syndicate_shuttle_boards)
var/obj/item/circuitboard/computer/syndicate_shuttle/board = V
board.challenge = TRUE
for(var/obj/machinery/computer/camera_advanced/shuttle_docker/D in GLOB.jam_on_wardec)
D.jammed = TRUE
GLOB.war_declared = TRUE
var/list/nukeops = get_antag_minds(/datum/antagonist/nukeop)
var/actual_players = GLOB.joined_player_list.len - nukeops.len
var/tc_malus = 0
if(actual_players < CHALLENGE_PLAYERS_TARGET)
tc_malus = FLOOR(((CHALLENGE_TELECRYSTALS / CHALLENGE_PLAYERS_TARGET) * (CHALLENGE_PLAYERS_TARGET - actual_players)) * TELECRYSTALS_MALUS_SCALING, 1)
new uplink_type(get_turf(user), user.key, CHALLENGE_TELECRYSTALS - tc_malus + CEILING(PLAYER_SCALING * actual_players, 1))
CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY))
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
mode.spend_threat(threat_spent)
mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1)
qdel(src)
/obj/item/nuclear_challenge/proc/check_allowed(mob/living/user)
if(declaring_war)
to_chat(user, "You are already in the process of declaring war! Make your mind up.")
return FALSE
if(!user.onSyndieBase())
to_chat(user, "You have to be at your base to use this.")
return FALSE
if(world.time-SSticker.round_start_time > CHALLENGE_TIME_LIMIT)
to_chat(user, "It's too late to declare hostilities. Your benefactors are already busy with other schemes. You'll have to make do with what you have on hand.")
return FALSE
for(var/V in GLOB.syndicate_shuttle_boards)
var/obj/item/circuitboard/computer/syndicate_shuttle/board = V
if(board.moved)
to_chat(user, "The shuttle has already been moved! You have forfeit the right to declare war.")
return FALSE
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
return FALSE
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
return FALSE
return TRUE
/obj/item/nuclear_challenge/clownops
uplink_type = /obj/item/uplink/clownop
#undef CHALLENGE_TELECRYSTALS
#undef CHALLENGE_TIME_LIMIT
#undef CHALLENGE_PLAYERS_TARGET
#undef TELECRYSTALS_MALUS_SCALING
#undef CHALLENGE_SHUTTLE_DELAY
@@ -1,655 +0,0 @@
/obj/machinery/nuclearbomb
name = "nuclear fission explosive"
desc = "You probably shouldn't stick around to see if this is armed."
icon = 'icons/obj/machines/nuke.dmi'
icon_state = "nuclearbomb_base"
anchored = FALSE
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/timer_set = 90
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
var/ui_style = "nanotrasen"
var/numeric_input = ""
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
var/detonation_timer = null
var/r_code = "ADMIN"
var/yes_code = FALSE
var/safety = TRUE
var/obj/item/disk/nuclear/auth = null
use_power = NO_POWER_USE
var/previous_level = ""
var/obj/item/nuke_core/core = null
var/deconstruction_state = NUKESTATE_INTACT
var/lights = ""
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
/obj/machinery/nuclearbomb/Initialize()
. = ..()
countdown = new(src)
GLOB.nuke_list += src
core = new /obj/item/nuke_core(src)
STOP_PROCESSING(SSobj, core)
update_icon()
GLOB.poi_list |= src
previous_level = get_security_level()
/obj/machinery/nuclearbomb/Destroy()
safety = FALSE
if(!exploding)
// If we're not exploding, set the alert level back to normal
set_safety()
GLOB.poi_list -= src
GLOB.nuke_list -= src
QDEL_NULL(countdown)
QDEL_NULL(core)
. = ..()
/obj/machinery/nuclearbomb/examine(mob/user)
. = ..()
if(exploding)
to_chat(user, "It is in the process of exploding. Perhaps reviewing your affairs is in order.")
if(timing)
to_chat(user, "There are [get_time_left()] seconds until detonation.")
/obj/machinery/nuclearbomb/selfdestruct
name = "station self-destruct terminal"
desc = "For when it all gets too much to bear. Do not taunt."
icon = 'icons/obj/machines/nuke_terminal.dmi'
icon_state = "nuclearbomb_base"
anchored = TRUE //stops it being moved
/obj/machinery/nuclearbomb/syndicate
//ui_style = "syndicate" // actually the nuke op bomb is a stole nt bomb
/obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station)
var/datum/game_mode/nuclear/NM = SSticker.mode
switch(off_station)
if(0)
if(istype(NM) && !NM.nuke_team.syndies_escaped())
return CINEMATIC_ANNIHILATION
else
return CINEMATIC_NUKE_WIN
if(1)
return CINEMATIC_NUKE_MISS
if(2)
return CINEMATIC_NUKE_FAR
return CINEMATIC_NUKE_FAR
/obj/machinery/nuclearbomb/proc/disk_check(obj/item/disk/nuclear/D)
if(D.fake)
say("Authentication failure; disk not recognised.")
return FALSE
else
return TRUE
/obj/machinery/nuclearbomb/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/disk/nuclear))
if(!disk_check(I))
return
if(!user.transferItemToLoc(I, src))
return
auth = I
add_fingerprint(user)
return
switch(deconstruction_state)
if(NUKESTATE_INTACT)
if(istype(I, /obj/item/screwdriver/nuke))
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
to_chat(user, "<span class='notice'>You remove the screws from [src]'s front panel.</span>")
update_icon()
return
if(NUKESTATE_PANEL_REMOVED)
if(istype(I, /obj/item/weldingtool))
if(!I.tool_start_check(user, amount=1))
return
to_chat(user, "<span class='notice'>You start cutting [src]'s inner plate...</span>")
if(I.use_tool(src, user, 80, volume=100, amount=1))
to_chat(user, "<span class='notice'>You cut [src]'s inner plate.</span>")
deconstruction_state = NUKESTATE_WELDED
update_icon()
return
if(NUKESTATE_CORE_EXPOSED)
if(istype(I, /obj/item/nuke_core_container))
var/obj/item/nuke_core_container/core_box = I
to_chat(user, "<span class='notice'>You start loading the plutonium core into [core_box]...</span>")
if(do_after(user,50,target=src))
if(core_box.load(core, user))
to_chat(user, "<span class='notice'>You load the plutonium core into [core_box].</span>")
deconstruction_state = NUKESTATE_CORE_REMOVED
update_icon()
core = null
else
to_chat(user, "<span class='warning'>You fail to load the plutonium core into [core_box]. [core_box] has already been used!</span>")
return
if(istype(I, /obj/item/stack/sheet/metal))
if(!I.tool_start_check(user, amount=20))
return
to_chat(user, "<span class='notice'>You begin repairing [src]'s inner metal plate...</span>")
if(I.use_tool(src, user, 100, amount=20))
to_chat(user, "<span class='notice'>You repair [src]'s inner metal plate. The radiation is contained.</span>")
deconstruction_state = NUKESTATE_PANEL_REMOVED
STOP_PROCESSING(SSobj, core)
update_icon()
return
. = ..()
/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/tool)
. = FALSE
switch(deconstruction_state)
if(NUKESTATE_UNSCREWED)
to_chat(user, "<span class='notice'>You start removing [src]'s front panel...</span>")
if(tool.use_tool(src, user, 30, volume=100))
to_chat(user, "<span class='notice'>You remove [src]'s front panel.</span>")
deconstruction_state = NUKESTATE_PANEL_REMOVED
update_icon()
return TRUE
if(NUKESTATE_WELDED)
to_chat(user, "<span class='notice'>You start prying off [src]'s inner plate...</span>")
if(tool.use_tool(src, user, 30, volume=100))
to_chat(user, "<span class='notice'>You pry off [src]'s inner plate. You can see the core's green glow!</span>")
deconstruction_state = NUKESTATE_CORE_EXPOSED
update_icon()
START_PROCESSING(SSobj, core)
return TRUE
/obj/machinery/nuclearbomb/proc/get_nuke_state()
if(exploding)
return NUKE_ON_EXPLODING
if(timing)
return NUKE_ON_TIMING
if(safety)
return NUKE_OFF_LOCKED
else
return NUKE_OFF_UNLOCKED
/obj/machinery/nuclearbomb/update_icon()
if(deconstruction_state == NUKESTATE_INTACT)
switch(get_nuke_state())
if(NUKE_OFF_LOCKED, NUKE_OFF_UNLOCKED)
icon_state = "nuclearbomb_base"
update_icon_interior()
update_icon_lights()
if(NUKE_ON_TIMING)
cut_overlays()
icon_state = "nuclearbomb_timing"
if(NUKE_ON_EXPLODING)
cut_overlays()
icon_state = "nuclearbomb_exploding"
else
icon_state = "nuclearbomb_base"
update_icon_interior()
update_icon_lights()
/obj/machinery/nuclearbomb/proc/update_icon_interior()
cut_overlay(interior)
switch(deconstruction_state)
if(NUKESTATE_UNSCREWED)
interior = "panel-unscrewed"
if(NUKESTATE_PANEL_REMOVED)
interior = "panel-removed"
if(NUKESTATE_WELDED)
interior = "plate-welded"
if(NUKESTATE_CORE_EXPOSED)
interior = "plate-removed"
if(NUKESTATE_CORE_REMOVED)
interior = "core-removed"
if(NUKESTATE_INTACT)
return
add_overlay(interior)
/obj/machinery/nuclearbomb/proc/update_icon_lights()
if(lights)
cut_overlay(lights)
switch(get_nuke_state())
if(NUKE_OFF_LOCKED)
lights = ""
return
if(NUKE_OFF_UNLOCKED)
lights = "lights-safety"
if(NUKE_ON_TIMING)
lights = "lights-timing"
if(NUKE_ON_EXPLODING)
lights = "lights-exploding"
add_overlay(lights)
/obj/machinery/nuclearbomb/process()
if(timing && !exploding)
if(detonation_timer < world.time)
explode()
else
var/volume = (get_time_left() <= 20 ? 30 : 5)
playsound(loc, 'sound/items/timer.ogg', volume, 0)
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nuclear_bomb", name, 500, 600, master_ui, state)
ui.set_style(ui_style)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
data["code_approved"] = yes_code
var/first_status
if(auth)
if(yes_code)
first_status = timing ? "Func/Set" : "Functional"
else
first_status = "Auth S2."
else
if(timing)
first_status = "Set"
else
first_status = "Auth S1."
var/second_status = exploded ? "Warhead triggered, thanks for flying Nanotrasen" : (safety ? "Safe" : "Engaged")
data["status1"] = first_status
data["status2"] = second_status
data["anchored"] = anchored
data["safety"] = safety
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
var/message = "AUTH"
if(auth)
message = "[numeric_input]"
if(yes_code)
message = "*****"
data["message"] = message
return data
/obj/machinery/nuclearbomb/ui_act(action, params)
if(..())
return
switch(action)
if("eject_disk")
if(auth && auth.loc == src)
auth.forceMove(get_turf(src))
auth = null
. = TRUE
if("insert_disk")
if(!auth)
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/disk/nuclear)
if(I && disk_check(I) && usr.transferItemToLoc(I, src))
auth = I
. = TRUE
if("keypad")
if(auth)
var/digit = params["digit"]
switch(digit)
if("R")
numeric_input = ""
yes_code = FALSE
. = TRUE
if("E")
if(numeric_input == r_code)
numeric_input = ""
yes_code = TRUE
. = TRUE
else
numeric_input = "ERROR"
if("0","1","2","3","4","5","6","7","8","9")
if(numeric_input != "ERROR")
numeric_input += digit
if(length(numeric_input) > 5)
numeric_input = "ERROR"
. = TRUE
if("timer")
if(auth && yes_code)
var/change = params["change"]
if(change == "reset")
timer_set = default_timer_set
else if(change == "decrease")
timer_set = max(minimum_timer_set, timer_set - 10)
else if(change == "increase")
timer_set = min(maximum_timer_set, timer_set + 10)
else if(change == "input")
var/user_input = input(usr, "Set time to detonation.", name) as null|num
if(!user_input)
return
var/N = text2num(user_input)
if(!N)
return
timer_set = CLAMP(N,minimum_timer_set,maximum_timer_set)
. = TRUE
if("safety")
if(auth && yes_code && !exploded)
set_safety()
if("anchor")
if(auth && yes_code)
set_anchor()
if("toggle_timer")
if(auth && yes_code && !safety && !exploded)
set_active()
/obj/machinery/nuclearbomb/proc/set_anchor()
if(isinspace() && !anchored)
to_chat(usr, "<span class='warning'>There is nothing to anchor to!</span>")
else
anchored = !anchored
/obj/machinery/nuclearbomb/proc/set_safety()
safety = !safety
if(safety)
if(timing)
set_security_level(previous_level)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(initial(S.mode))
S.alert = FALSE
timing = FALSE
detonation_timer = null
countdown.stop()
update_icon()
/obj/machinery/nuclearbomb/proc/set_active()
if(safety)
to_chat(usr, "<span class='danger'>The safety is still on.</span>")
return
timing = !timing
if(timing)
previous_level = get_security_level()
detonation_timer = world.time + (timer_set * 10)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
if(GLOB.war_declared)
var/area/A = get_area(src)
priority_announce("Alert: Unexpected increase in radiation levels near [A.name] ([src.x],[src.y],[src.z]). Please send an authorized radiation specialist to investigate.", "Sensory Nuclear Indexer Telemetry Calculation Helper")
else
detonation_timer = null
set_security_level(previous_level)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(initial(S.mode))
S.alert = FALSE
countdown.stop()
update_icon()
/obj/machinery/nuclearbomb/proc/get_time_left()
if(timing)
. = round(max(0, detonation_timer - world.time) / 10, 1)
else
. = timer_set
/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B)
if(exploding)
return
qdel(src)
/obj/machinery/nuclearbomb/tesla_act(power, tesla_flags)
..()
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
#define NUKERANGE 127
/obj/machinery/nuclearbomb/proc/explode()
if(safety)
timing = FALSE
return
exploding = TRUE
yes_code = FALSE
safety = TRUE
update_icon()
sound_to_playing_players('sound/machines/alarm.ogg')
if(SSticker && SSticker.mode)
SSticker.roundend_check_paused = TRUE
addtimer(CALLBACK(src, .proc/actually_explode), 100)
/obj/machinery/nuclearbomb/proc/actually_explode()
if(!core)
Cinematic(CINEMATIC_NUKE_NO_CORE,world)
SSticker.roundend_check_paused = FALSE
return
GLOB.enter_allowed = FALSE
var/off_station = 0
var/turf/bomb_location = get_turf(src)
var/area/A = get_area(bomb_location)
if(bomb_location && is_station_level(bomb_location.z))
if(istype(A, /area/space))
off_station = NUKE_NEAR_MISS
if((bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)))
off_station = NUKE_NEAR_MISS
else if(bomb_location.onSyndieBase())
off_station = NUKE_SYNDICATE_BASE
else
off_station = NUKE_MISS_STATION
if(off_station < 2)
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
//Cinematic
SSticker.mode.OnNukeExplosion(off_station)
really_actually_explode(off_station)
SSticker.roundend_check_paused = FALSE
/obj/machinery/nuclearbomb/proc/really_actually_explode(off_station)
Cinematic(get_cinematic_type(off_station),world,CALLBACK(SSticker,/datum/controller/subsystem/ticker/proc/station_explosion_detonation,src))
INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z)
/obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station)
if(off_station < 2)
return CINEMATIC_SELFDESTRUCT
else
return CINEMATIC_SELFDESTRUCT_MISS
/obj/machinery/nuclearbomb/beer
name = "Nanotrasen-brand nuclear fission explosive"
desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back."
proper_bomb = FALSE
var/obj/structure/reagent_dispensers/beerkeg/keg
/obj/machinery/nuclearbomb/beer/Initialize()
. = ..()
keg = new(src)
QDEL_NULL(core)
/obj/machinery/nuclearbomb/beer/examine(mob/user)
. = ..()
if(keg.reagents.total_volume)
to_chat(user, "<span class='notice'>It has [keg.reagents.total_volume] unit\s left.</span>")
else
to_chat(user, "<span class='danger'>It's empty.</span>")
/obj/machinery/nuclearbomb/beer/attackby(obj/item/W, mob/user, params)
if(W.is_refillable())
W.afterattack(keg, user, TRUE) // redirect refillable containers to the keg, allowing them to be filled
return TRUE // pretend we handled the attack, too.
if(istype(W, /obj/item/nuke_core_container))
to_chat(user, "<span class='notice'>[src] has had its plutonium core removed as a part of being decommissioned.</span>")
return TRUE
return ..()
/obj/machinery/nuclearbomb/beer/actually_explode()
var/turf/bomb_location = get_turf(src)
if(!bomb_location)
disarm()
return
if(is_station_level(bomb_location.z))
var/datum/round_event_control/E = locate(/datum/round_event_control/vent_clog/beer) in SSevents.control
if(E)
E.runEvent()
addtimer(CALLBACK(src, .proc/really_actually_explode), 110)
else
visible_message("<span class='notice'>[src] fizzes ominously.</span>")
addtimer(CALLBACK(src, .proc/fizzbuzz), 110)
/obj/machinery/nuclearbomb/beer/proc/disarm()
detonation_timer = null
exploding = FALSE
exploded = TRUE
set_security_level(previous_level)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(initial(S.mode))
S.alert = FALSE
countdown.stop()
update_icon()
/obj/machinery/nuclearbomb/beer/proc/fizzbuzz()
var/datum/reagents/R = new/datum/reagents(1000)
R.my_atom = src
R.add_reagent("beer", 100)
var/datum/effect_system/foam_spread/foam = new
foam.set_up(200, get_turf(src), R)
foam.start()
disarm()
/obj/machinery/nuclearbomb/beer/really_actually_explode()
disarm()
/proc/KillEveryoneOnZLevel(z)
if(!z)
return
for(var/mob/M in GLOB.mob_list)
if(M.stat != DEAD && M.z == z)
M.gib()
/*
This is here to make the tiles around the station mininuke change when it's armed.
*/
/obj/machinery/nuclearbomb/selfdestruct/set_anchor()
return
/obj/machinery/nuclearbomb/selfdestruct/set_active()
..()
if(timing)
SSmapping.add_nuke_threat(src)
else
SSmapping.remove_nuke_threat(src)
/obj/machinery/nuclearbomb/selfdestruct/set_safety()
..()
if(timing)
SSmapping.add_nuke_threat(src)
else
SSmapping.remove_nuke_threat(src)
//==========DAT FUKKEN DISK===============
/obj/item/disk
icon = 'icons/obj/module.dmi'
w_class = WEIGHT_CLASS_TINY
item_state = "card-id"
lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
icon_state = "datadisk0"
/obj/item/disk/nuclear
name = "nuclear authentication disk"
desc = "Better keep this safe."
icon_state = "nucleardisk"
persistence_replacement = /obj/item/disk/nuclear/fake
max_integrity = 250
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/fake = FALSE
var/turf/lastlocation
var/last_disk_move
/obj/item/disk/nuclear/Initialize()
. = ..()
if(!fake)
GLOB.poi_list |= src
last_disk_move = world.time
START_PROCESSING(SSobj, src)
/obj/item/disk/nuclear/ComponentInitialize()
. = ..()
AddComponent(/datum/component/stationloving, !fake)
/obj/item/disk/nuclear/process()
if(fake)
STOP_PROCESSING(SSobj, src)
CRASH("A fake nuke disk tried to call process(). Who the fuck and how the fuck")
var/turf/newturf = get_turf(src)
if(newturf && lastlocation == newturf)
if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop))
loneop.weight += 1
if(loneop.weight % 5 == 0)
message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].")
log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].")
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
if(loneop.weight % 5 == 0)
message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].")
log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].")
/obj/item/disk/nuclear/examine(mob/user)
. = ..()
if(!fake)
return
var/ghost = isobserver(user)
var/captain = user.mind && user.mind.assigned_role == "Captain"
var/nukie = user.mind && user.mind.has_antag_datum(/datum/antagonist/nukeop)
if(ghost || captain || nukie)
to_chat(user, "<span class='warning'>The serial numbers on [src] are incorrect.</span>")
/obj/item/disk/nuclear/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/claymore/highlander) && !fake)
var/obj/item/claymore/highlander/H = I
if(H.nuke_disk)
to_chat(user, "<span class='notice'>Wait... what?</span>")
qdel(H.nuke_disk)
H.nuke_disk = null
return
user.visible_message("<span class='warning'>[user] captures [src]!</span>", "<span class='userdanger'>You've got the disk! Defend it with your life!</span>")
forceMove(H)
H.nuke_disk = src
return TRUE
return ..()
/obj/item/disk/nuclear/Destroy(force=FALSE)
// respawning is handled in /obj/Destroy()
if(force)
GLOB.poi_list -= src
. = ..()
/obj/item/disk/nuclear/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(src, 'sound/machines/alarm.ogg', 50, -1, 1)
for(var/i in 1 to 100)
addtimer(CALLBACK(user, /atom/proc/add_atom_colour, (i % 2)? "#00FF00" : "#FF0000", ADMIN_COLOUR_PRIORITY), i)
addtimer(CALLBACK(src, .proc/manual_suicide, user), 101)
return MANUAL_SUICIDE
/obj/item/disk/nuclear/proc/manual_suicide(mob/living/user)
user.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
user.visible_message("<span class='suicide'>[user] was destroyed by the nuclear blast!</span>")
user.adjustOxyLoss(200)
user.death(0)
/obj/item/disk/nuclear/fake
fake = TRUE
-385
View File
@@ -1,385 +0,0 @@
/datum/antagonist/nukeop
name = "Nuclear Operative"
roundend_category = "syndicate operatives" //just in case
antagpanel_category = "NukeOp"
job_rank = ROLE_OPERATIVE
antag_moodlet = /datum/mood_event/focused
var/datum/team/nuclear/nuke_team
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
var/nukeop_outfit = /datum/outfit/syndicate
can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station.
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
opshud.join_hud(M)
set_antag_hud(M, "synd")
/datum/antagonist/nukeop/proc/update_synd_icons_removed(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
opshud.leave_hud(M)
set_antag_hud(M, null)
/datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_synd_icons_added(M)
/datum/antagonist/nukeop/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_synd_icons_removed(M)
/datum/antagonist/nukeop/proc/equip_op()
if(!ishuman(owner.current))
return
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs
H.equipOutfit(nukeop_outfit)
return TRUE
/datum/antagonist/nukeop/greet()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
to_chat(owner, "<span class='notice'>You are a [nuke_team ? nuke_team.syndicate_name : "syndicate"] agent!</span>")
owner.announce_objectives()
return
/datum/antagonist/nukeop/on_gain()
give_alias()
forge_objectives()
. = ..()
equip_op()
memorize_code()
if(send_to_spawnpoint)
move_to_spawnpoint()
/datum/antagonist/nukeop/get_team()
return nuke_team
/datum/antagonist/nukeop/proc/assign_nuke()
if(nuke_team && !nuke_team.tracked_nuke)
nuke_team.memorized_code = random_nukecode()
var/obj/machinery/nuclearbomb/syndicate/nuke = locate() in GLOB.nuke_list
if(nuke)
nuke_team.tracked_nuke = nuke
if(nuke.r_code == "ADMIN")
nuke.r_code = nuke_team.memorized_code
else //Already set by admins/something else?
nuke_team.memorized_code = nuke.r_code
for(var/obj/machinery/nuclearbomb/beer/beernuke in GLOB.nuke_list)
beernuke.r_code = nuke_team.memorized_code
else
stack_trace("Syndicate nuke not found during nuke team creation.")
nuke_team.memorized_code = null
/datum/antagonist/nukeop/proc/give_alias()
if(nuke_team && nuke_team.syndicate_name)
var/number = 1
number = nuke_team.members.Find(owner)
owner.current.real_name = "[nuke_team.syndicate_name] Operative #[number]"
/datum/antagonist/nukeop/proc/memorize_code()
if(nuke_team && nuke_team.tracked_nuke && nuke_team.memorized_code)
antag_memory += "<B>[nuke_team.tracked_nuke] Code</B>: [nuke_team.memorized_code]<br>"
to_chat(owner, "The nuclear authorization code is: <B>[nuke_team.memorized_code]</B>")
else
to_chat(owner, "Unfortunately the syndicate was unable to provide you with nuclear authorization code.")
/datum/antagonist/nukeop/proc/forge_objectives()
if(nuke_team)
objectives |= nuke_team.objectives
/datum/antagonist/nukeop/proc/move_to_spawnpoint()
var/team_number = 1
if(nuke_team)
team_number = nuke_team.members.Find(owner)
owner.current.forceMove(GLOB.nukeop_start[((team_number - 1) % GLOB.nukeop_start.len) + 1])
/datum/antagonist/nukeop/leader/move_to_spawnpoint()
owner.current.forceMove(pick(GLOB.nukeop_leader_start))
/datum/antagonist/nukeop/create_team(datum/team/nuclear/new_team)
if(!new_team)
if(!always_new_team)
for(var/datum/antagonist/nukeop/N in GLOB.antagonists)
if(!N.owner)
continue
if(N.nuke_team)
nuke_team = N.nuke_team
return
nuke_team = new /datum/team/nuclear
nuke_team.update_objectives()
assign_nuke() //This is bit ugly
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
nuke_team = new_team
/datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin)
new_owner.assigned_role = ROLE_SYNDICATE
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has nuke op'ed [new_owner.current].")
log_admin("[key_name(admin)] has nuke op'ed [new_owner.current].")
/datum/antagonist/nukeop/get_admin_commands()
. = ..()
.["Send to base"] = CALLBACK(src,.proc/admin_send_to_base)
.["Tell code"] = CALLBACK(src,.proc/admin_tell_code)
/datum/antagonist/nukeop/proc/admin_send_to_base(mob/admin)
owner.current.forceMove(pick(GLOB.nukeop_start))
/datum/antagonist/nukeop/proc/admin_tell_code(mob/admin)
var/code
for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines)
if (length(bombue.r_code) <= 5 && bombue.r_code != initial(bombue.r_code))
code = bombue.r_code
break
if (code)
antag_memory += "<B>Syndicate Nuclear Bomb Code</B>: [code]<br>"
to_chat(owner.current, "The nuclear authorization code is: <B>[code]</B>")
else
to_chat(admin, "<span class='danger'>No valid nuke found!</span>")
/datum/antagonist/nukeop/leader
name = "Nuclear Operative Leader"
nukeop_outfit = /datum/outfit/syndicate/leader
always_new_team = TRUE
var/title
/datum/antagonist/nukeop/leader/memorize_code()
..()
if(nuke_team && nuke_team.memorized_code)
var/obj/item/paper/P = new
P.info = "The nuclear authorization code is: <b>[nuke_team.memorized_code]</b>"
P.name = "nuclear bomb code"
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
P.forceMove(get_turf(H))
else
H.put_in_hands(P, TRUE)
H.update_icons()
/datum/antagonist/nukeop/leader/give_alias()
title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")
if(nuke_team && nuke_team.syndicate_name)
owner.current.real_name = "[nuke_team.syndicate_name] [title]"
else
owner.current.real_name = "Syndicate [title]"
/datum/antagonist/nukeop/leader/greet()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
to_chat(owner, "<B>You are the Syndicate [title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.</B>")
to_chat(owner, "<B>If you feel you are not up to this task, give your ID to another operative.</B>")
to_chat(owner, "<B>In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.</B>")
owner.announce_objectives()
addtimer(CALLBACK(src, .proc/nuketeam_name_assign), 1)
/datum/antagonist/nukeop/leader/proc/nuketeam_name_assign()
if(!nuke_team)
return
nuke_team.rename_team(ask_name())
/datum/team/nuclear/proc/rename_team(new_name)
syndicate_name = new_name
name = "[syndicate_name] Team"
for(var/I in members)
var/datum/mind/synd_mind = I
var/mob/living/carbon/human/H = synd_mind.current
if(!istype(H))
continue
var/chosen_name = H.dna.species.random_name(H.gender,0,syndicate_name)
H.fully_replace_character_name(H.real_name,chosen_name)
/datum/antagonist/nukeop/leader/proc/ask_name()
var/randomname = pick(GLOB.last_names)
var/newname = stripped_input(owner.current,"You are the nuke operative [title]. Please choose a last name for your family.", "Name change",randomname)
if (!newname)
newname = randomname
else
newname = reject_bad_name(newname)
if(!newname)
newname = randomname
return capitalize(newname)
/datum/antagonist/nukeop/lone
name = "Lone Operative"
always_new_team = TRUE
send_to_spawnpoint = FALSE //Handled by event
nukeop_outfit = /datum/outfit/syndicate/lone
/datum/antagonist/nukeop/lone/assign_nuke()
if(nuke_team && !nuke_team.tracked_nuke)
nuke_team.memorized_code = random_nukecode()
var/obj/machinery/nuclearbomb/selfdestruct/nuke = locate() in GLOB.nuke_list
if(nuke)
nuke_team.tracked_nuke = nuke
if(nuke.r_code == "ADMIN")
nuke.r_code = nuke_team.memorized_code
else //Already set by admins/something else?
nuke_team.memorized_code = nuke.r_code
else
stack_trace("Station self destruct not found during lone op team creation.")
nuke_team.memorized_code = null
/datum/antagonist/nukeop/reinforcement
send_to_spawnpoint = FALSE
nukeop_outfit = /datum/outfit/syndicate/no_crystals
/datum/team/nuclear
var/syndicate_name
var/obj/machinery/nuclearbomb/tracked_nuke
var/core_objective = /datum/objective/nuclear
var/memorized_code
/datum/team/nuclear/New()
..()
syndicate_name = syndicate_name()
/datum/team/nuclear/proc/update_objectives()
if(core_objective)
var/datum/objective/O = new core_objective
O.team = src
objectives += O
/datum/team/nuclear/proc/disk_rescued()
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
//If emergency shuttle is in transit disk is only safe on it
if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
if(!SSshuttle.emergency.is_in_shuttle_bounds(D))
return FALSE
//If shuttle escaped check if it's on centcom side
else if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME)
if(!D.onCentCom())
return FALSE
else //Otherwise disk is safe when on station
var/turf/T = get_turf(D)
if(!T || !is_station_level(T.z))
return FALSE
return TRUE
/datum/team/nuclear/proc/operatives_dead()
for(var/I in members)
var/datum/mind/operative_mind = I
if(ishuman(operative_mind.current) && (operative_mind.current.stat != DEAD))
return FALSE
return TRUE
/datum/team/nuclear/proc/syndies_escaped()
var/obj/docking_port/mobile/S = SSshuttle.getShuttle("syndicate")
var/obj/docking_port/stationary/transit/T = locate() in S.loc
return S && (is_centcom_level(S.z) || T)
/datum/team/nuclear/proc/get_result()
var/evacuation = EMERGENCY_ESCAPED_OR_ENDGAMED
var/disk_rescued = disk_rescued()
var/syndies_didnt_escape = !syndies_escaped()
var/station_was_nuked = SSticker.mode.station_was_nuked
var/nuke_off_station = SSticker.mode.nuke_off_station
if(nuke_off_station == NUKE_SYNDICATE_BASE)
return NUKE_RESULT_FLUKE
else if(station_was_nuked && !syndies_didnt_escape)
return NUKE_RESULT_NUKE_WIN
else if (station_was_nuked && syndies_didnt_escape)
return NUKE_RESULT_NOSURVIVORS
else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
return NUKE_RESULT_WRONG_STATION
else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
return NUKE_RESULT_WRONG_STATION_DEAD
else if ((disk_rescued && evacuation) && operatives_dead())
return NUKE_RESULT_CREW_WIN_SYNDIES_DEAD
else if (disk_rescued)
return NUKE_RESULT_CREW_WIN
else if (!disk_rescued && operatives_dead())
return NUKE_RESULT_DISK_LOST
else if (!disk_rescued && evacuation)
return NUKE_RESULT_DISK_STOLEN
else
return //Undefined result
/datum/team/nuclear/roundend_report()
var/list/parts = list()
parts += "<span class='header'>[syndicate_name] Operatives:</span>"
switch(get_result())
if(NUKE_RESULT_FLUKE)
parts += "<span class='redtext big'>Humiliating Syndicate Defeat</span>"
parts += "<B>The crew of [station_name()] gave [syndicate_name] operatives back their bomb! The syndicate base was destroyed!</B> Next time, don't lose the nuke!"
if(NUKE_RESULT_NUKE_WIN)
parts += "<span class='greentext big'>Syndicate Major Victory!</span>"
parts += "<B>[syndicate_name] operatives have destroyed [station_name()]!</B>"
if(NUKE_RESULT_NOSURVIVORS)
parts += "<span class='neutraltext big'>Total Annihilation</span>"
parts += "<B>[syndicate_name] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion.</B> Next time, don't lose the disk!"
if(NUKE_RESULT_WRONG_STATION)
parts += "<span class='redtext big'>Crew Minor Victory</span>"
parts += "<B>[syndicate_name] operatives secured the authentication disk but blew up something that wasn't [station_name()].</B> Next time, don't do that!"
if(NUKE_RESULT_WRONG_STATION_DEAD)
parts += "<span class='redtext big'>[syndicate_name] operatives have earned Darwin Award!</span>"
parts += "<B>[syndicate_name] operatives blew up something that wasn't [station_name()] and got caught in the explosion.</B> Next time, don't do that!"
if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
parts += "<span class='redtext big'>Crew Major Victory!</span>"
parts += "<B>The Research Staff has saved the disk and killed the [syndicate_name] Operatives</B>"
if(NUKE_RESULT_CREW_WIN)
parts += "<span class='redtext big'>Crew Major Victory</span>"
parts += "<B>The Research Staff has saved the disk and stopped the [syndicate_name] Operatives!</B>"
if(NUKE_RESULT_DISK_LOST)
parts += "<span class='neutraltext big'>Neutral Victory!</span>"
parts += "<B>The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name] Operatives!</B>"
if(NUKE_RESULT_DISK_STOLEN)
parts += "<span class='greentext big'>Syndicate Minor Victory!</span>"
parts += "<B>[syndicate_name] operatives survived the assault but did not achieve the destruction of [station_name()].</B> Next time, don't lose the disk!"
else
parts += "<span class='neutraltext big'>Neutral Victory</span>"
parts += "<B>Mission aborted!</B>"
var/text = "<br><span class='header'>The syndicate operatives were:</span>"
var/purchases = ""
var/TC_uses = 0
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
for(var/I in members)
var/datum/mind/syndicate = I
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[syndicate.key]
if(H)
TC_uses += H.total_spent
purchases += H.generate_render(show_key = FALSE)
text += printplayerlist(members)
text += "<br>"
text += "(Syndicates used [TC_uses] TC) [purchases]"
if(TC_uses == 0 && SSticker.mode.station_was_nuked && !operatives_dead())
text += "<BIG>[icon2html('icons/badass.dmi', world, "badass")]</BIG>"
parts += text
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
/datum/team/nuclear/antag_listing_name()
if(syndicate_name)
return "[syndicate_name] Syndicates"
else
return "Syndicates"
/datum/team/nuclear/antag_listing_entry()
var/disk_report = "<b>Nuclear Disk(s)</b><br>"
disk_report += "<table cellspacing=5>"
for(var/obj/item/disk/nuclear/N in GLOB.poi_list)
disk_report += "<tr><td>[N.name], "
var/atom/disk_loc = N.loc
while(!isturf(disk_loc))
if(ismob(disk_loc))
var/mob/M = disk_loc
disk_report += "carried by <a href='?_src_=holder;[HrefToken()];adminplayeropts=[REF(M)]'>[M.real_name]</a> "
if(isobj(disk_loc))
var/obj/O = disk_loc
disk_report += "in \a [O.name] "
disk_loc = disk_loc.loc
disk_report += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])</td><td><a href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(N)]'>FLW</a></td></tr>"
disk_report += "</table>"
var/common_part = ..()
return common_part + disk_report
/datum/team/nuclear/is_gamemode_hero()
return SSticker.mode.name == "nuclear emergency"
@@ -1,43 +0,0 @@
/datum/antagonist/official
name = "CentCom Official"
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
can_hijack = HIJACK_PREVENT
/datum/antagonist/official/greet()
to_chat(owner, "<B><font size=3 color=red>You are a CentCom Official.</font></B>")
if (ert_team)
to_chat(owner, "Central Command is sending you to [station_name()] with the task: [ert_team.mission.explanation_text]")
else
to_chat(owner, "Central Command is sending you to [station_name()] with the task: [mission.explanation_text]")
/datum/antagonist/official/proc/equip_official()
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
H.equipOutfit(/datum/outfit/centcom_official)
if(CONFIG_GET(flag/enforce_human_authority))
H.set_species(/datum/species/human)
/datum/antagonist/official/create_team(datum/team/new_team)
if(istype(new_team))
ert_team = new_team
/datum/antagonist/official/proc/forge_objectives()
if (ert_team)
objectives |= ert_team.objectives
else if (!mission)
var/datum/objective/missionobj = new
missionobj.owner = owner
missionobj.explanation_text = "Conduct a routine performance review of [station_name()] and its Captain."
missionobj.completed = 1
mission = missionobj
objectives |= mission
/datum/antagonist/official/on_gain()
forge_objectives()
. = ..()
equip_official()
@@ -1,155 +0,0 @@
#define INITIAL_CRYSTALS 5 // initial telecrystals in the boss' uplink
// Syndicate mutineer agents. They're agents selected by the Syndicate to take control of stations when assault teams like nuclear operatives cannot be sent.
// They sent teams made of 3 agents, of which only one is woke up at round start. The others are, lore-wise, sleeping agents and must be implanted with the converter to wake up.
// Mechanics wise, it's just 1 dude per team and he can convert maximum 2 more people of his choice, based on the implanter use var, Upon converting, the newly made guys are given access
// to a storage implant they came with when the Syndicate sent them aboard, with one random low-cost traitor item. The initial agent also has this. The only difference between
// initial agents and converted ones is that the initial agent has the items required to convert people and the AI.
/datum/antagonist/overthrow
name = "Syndicate mutineer"
roundend_category = "syndicate mutineers"
antagpanel_category = "Syndicate Mutineers"
job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings
var/datum/team/overthrow/team
var/static/list/possible_useful_items
// Overthrow agent. The idea is based on sleeping agents being sent as crewmembers, with one for each team that starts woken up who can also wake up others with their converter implant.
// Obviously they can just convert anyone, the idea of sleeping agents is just lore. This also explains why this antag type has no deconversion way: they're traitors. Traitors cannot be
// deconverted.
// Generates the list of possible items for the storage implant given on_gain
/datum/antagonist/overthrow/New()
..()
if(!possible_useful_items)
possible_useful_items = list(/obj/item/gun/ballistic/automatic/pistol, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/pen/edagger, /obj/item/pen/sleepy, \
/obj/item/soap/syndie, /obj/item/card/id/syndicate, /obj/item/storage/box/syndie_kit/chameleon)
// Sets objectives, equips all antags with the storage implant.
/datum/antagonist/overthrow/on_gain()
objectives += team.objectives
..()
owner.announce_objectives()
equip_overthrow()
owner.special_role = ROLE_OVERTHROW
/datum/antagonist/overthrow/on_removal()
owner.special_role = null
..()
// Creates the overthrow team, or sets it. The objectives are static for all the team members.
/datum/antagonist/overthrow/create_team(datum/team/overthrowers)
if(!overthrowers)
team = new()
team.add_member(owner)
name_team()
team.create_objectives()
else
team = overthrowers
team.add_member(owner)
// Used to name the team at round start. If no name is passed, a syndicate themed one is given randomly.
/datum/antagonist/overthrow/proc/name_team()
var/team_name = stripped_input(owner.current, "Name your team:", "Team name", , MAX_NAME_LEN)
var/already_taken = FALSE
for(var/datum/antagonist/overthrow/O in GLOB.antagonists)
if(team_name == O.name)
already_taken = TRUE
break
if(!team_name || already_taken) // basic protection against two teams with the same name. This could still happen with extreme unluck due to syndicate_name() but it shouldn't break anything.
team.name = syndicate_name()
to_chat(owner, "<span class='danger'>Since you gave [already_taken ? "an already used" : "no"] name, your team's name has been randomly generated: [team.name]!</span>")
return
team.name = team_name
// CLOWNMUT removal and HUD creation/being given
/datum/antagonist/overthrow/apply_innate_effects()
..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
if(!silent)
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
traitor_mob.dna.remove_mutation(CLOWNMUT)
update_overthrow_icons_added()
// The opposite
/datum/antagonist/overthrow/remove_innate_effects()
update_overthrow_icons_removed()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
traitor_mob.dna.add_mutation(CLOWNMUT)
..()
/datum/antagonist/overthrow/get_admin_commands()
. = ..()
.["Give storage with random item"] = CALLBACK(src,.proc/equip_overthrow)
.["Give overthrow boss equip"] = CALLBACK(src,.proc/equip_initial_overthrow_agent)
// Dynamically creates the HUD for the team if it doesn't exist already, inserting it into the global huds list, and assigns it to the user. The index is saved into a var owned by the team datum.
/datum/antagonist/overthrow/proc/update_overthrow_icons_added(datum/mind/traitor_mind)
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num]
if(!overthrowhud)
overthrowhud = new()
team.hud_entry_num = GLOB.huds.len + 1 // the index of the hud inside huds list
GLOB.huds += overthrowhud
overthrowhud.join_hud(owner.current)
set_antag_hud(owner.current, "traitor")
// Removes hud. Destroying the hud datum itself in case the team is deleted is done on team Destroy().
/datum/antagonist/overthrow/proc/update_overthrow_icons_removed(datum/mind/traitor_mind)
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num]
if(overthrowhud)
overthrowhud.leave_hud(owner.current)
set_antag_hud(owner.current, null)
// Gives the storage implant with a random item. They're sleeping agents, after all.
/datum/antagonist/overthrow/proc/equip_overthrow()
if(!owner || !owner.current || !ishuman(owner.current)) // only equip existing human overthrow members. This excludes the AI, in particular.
return
var/obj/item/implant/storage/S = locate(/obj/item/implant/storage) in owner.current.implants
if(!S)
S = new
S.implant(owner.current)
var/I = pick(possible_useful_items)
if(ispath(I)) // in case some admin decides to fuck the list up for fun
I = new I()
SEND_SIGNAL(S, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)
// Equip the initial overthrow agent. Manually called in overthrow gamemode, when the initial agents are chosen. Gives uplink, AI module board and the converter.
/datum/antagonist/overthrow/proc/equip_initial_overthrow_agent()
if(!owner || !owner.current || !ishuman(owner.current))
return
var/mob/living/carbon/human/H = owner.current
// Give uplink
var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src)
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
uplink.telecrystals = INITIAL_CRYSTALS
// Give AI hacking board
var/obj/item/aiModule/core/full/overthrow/O = new(H)
var/list/slots = list (
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/where = H.equip_in_one_of_slots(O, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you the AI module.")
else
to_chat(H, "Use the AI board in your [where] to take control of the AI, as requested by the Syndicate.")
// Give the implant converter
var/obj/item/overthrow_converter/I = new(H)
where = H.equip_in_one_of_slots(I, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you a converter implant.")
else
to_chat(H, "Use the implanter in your [where] to wake up sleeping syndicate agents, so that they can aid you.")
/datum/antagonist/overthrow/get_team()
return team
/datum/antagonist/overthrow/greet()
to_chat(owner.current, "<B><font size=3 color=red>You are a syndicate sleeping agent!</font> <font size=2 color=red>Your job is to stage a swift, fairly bloodless coup. Your team has a two-use converter that can be used to convert \
anyone you want, although mind shield implants need to be removed firstly for it to work. Your team also has a special version of the Syndicate module to be used to convert the AI, too. You \
will be able to use the special storage implant you came aboard with, which contains a random, cheap item from our special selection which will aid in your mission. \
Your team objective is to deal with the heads, the AI and a special target who angered us for several reasons which you're not entitled to know. Converting to your team will let us \
take control of the station faster, so it should be prioritized, especially over killing, which should be avoided where possible. The other Syndicate teams are NOT friends and should not \
be trusted.</font></B>")
@@ -1,43 +0,0 @@
/datum/team/overthrow
name = "overthrow" // The team name is set on creation by the leader.
member_name = "syndicate agent"
var/hud_entry_num // A number holding the hud's index inside 'huds' global list. Gets set on hud update, if a hud doesn't exist already. Must be a number, otherwise BYOND shits up with assoc lists and everything goes to hell.
/datum/team/overthrow/Destroy()
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[hud_entry_num]
GLOB.huds -= GLOB.huds[hud_entry_num]
qdel(overthrowhud)
. = ..()
/datum/team/overthrow/proc/create_objectives()
// Heads objective
var/datum/objective/overthrow/heads/heads = new()
heads.team = src
heads.find_target()
objectives += heads
// AI objective
var/datum/objective/overthrow/AI/AI = new()
AI.team = src
AI.update_explanation_text()
objectives += AI
// Target objective
var/datum/objective/overthrow/target/target = new()
target.team = src
target.find_target()
objectives += target
addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE)
/datum/team/overthrow/proc/update_objectives()
var/datum/objective/overthrow/heads/heads_obj = locate() in objectives
if(!heads_obj)
heads_obj = new()
heads_obj.team = src
objectives += heads_obj
for(var/i in members)
var/datum/mind/M = i
var/datum/antagonist/overthrow/O = M.has_antag_datum(/datum/antagonist/overthrow)
if(O)
O.objectives += heads_obj
heads_obj.find_targets()
addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE)
-107
View File
@@ -1,107 +0,0 @@
/datum/antagonist/pirate
name = "Space Pirate"
job_rank = ROLE_TRAITOR
roundend_category = "space pirates"
antagpanel_category = "Pirate"
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
to_chat(owner, "<span class='boldannounce'>You are a Space Pirate!</span>")
to_chat(owner, "<B>The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot.</B>")
owner.announce_objectives()
/datum/antagonist/pirate/get_team()
return crew
/datum/antagonist/pirate/create_team(datum/team/pirate/new_team)
if(!new_team)
for(var/datum/antagonist/pirate/P in GLOB.antagonists)
if(!P.owner)
continue
if(P.crew)
crew = P.crew
return
if(!new_team)
crew = new /datum/team/pirate
crew.forge_objectives()
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
crew = new_team
/datum/antagonist/pirate/on_gain()
if(crew)
objectives |= crew.objectives
. = ..()
/datum/team/pirate
name = "Pirate crew"
/datum/team/pirate/proc/forge_objectives()
var/datum/objective/loot/getbooty = new()
getbooty.team = src
for(var/obj/machinery/computer/piratepad_control/P in GLOB.machines)
var/area/A = get_area(P)
if(istype(A,/area/shuttle/pirate))
getbooty.cargo_hold = P
break
getbooty.update_explanation_text()
objectives += getbooty
for(var/datum/mind/M in members)
var/datum/antagonist/pirate/P = M.has_antag_datum(/datum/antagonist/pirate)
if(P)
P.objectives |= objectives
/datum/objective/loot
var/obj/machinery/computer/piratepad_control/cargo_hold
explanation_text = "Acquire valuable loot and store it in designated area."
var/target_value = 50000
/datum/objective/loot/update_explanation_text()
if(cargo_hold)
var/area/storage_area = get_area(cargo_hold)
explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name] cargo hold."
/datum/objective/loot/proc/loot_listing()
//Lists notable loot.
if(!cargo_hold || !cargo_hold.total_report)
return "Nothing"
cargo_hold.total_report.total_value = sortTim(cargo_hold.total_report.total_value, cmp = /proc/cmp_numeric_dsc, associative = TRUE)
var/count = 0
var/list/loot_texts = list()
for(var/datum/export/E in cargo_hold.total_report.total_value)
if(++count > 5)
break
loot_texts += E.total_printout(cargo_hold.total_report,notes = FALSE)
return loot_texts.Join(", ")
/datum/objective/loot/proc/get_loot_value()
return cargo_hold.points
/datum/objective/loot/check_completion()
return ..() || get_loot_value() >= target_value
/datum/team/pirate/roundend_report()
var/list/parts = list()
parts += "<span class='header'>Space Pirates were:</span>"
var/all_dead = TRUE
for(var/datum/mind/M in members)
if(considered_alive(M))
all_dead = FALSE
parts += printplayerlist(members)
parts += "Loot stolen: "
var/datum/objective/loot/L = locate() in objectives
parts += L.loot_listing()
parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
if(L.check_completion() && !all_dead)
parts += "<span class='greentext big'>The pirate crew was successful!</span>"
else
parts += "<span class='redtext big'>The pirate crew has failed.</span>"
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
@@ -1,466 +0,0 @@
//Revenants: based off of wraiths from Goon
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invisible
//Don't hear deadchat and are NOT normal ghosts
//Admin-spawn or random event
#define INVISIBILITY_REVENANT 50
#define REVENANT_NAME_FILE "revenant_names.json"
/mob/living/simple_animal/revenant
name = "revenant"
desc = "A malevolent spirit."
icon = 'icons/mob/mob.dmi'
icon_state = "revenant_idle"
var/icon_idle = "revenant_idle"
var/icon_reveal = "revenant_revealed"
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
var/stasis = FALSE
mob_biotypes = list(MOB_SPIRIT)
incorporeal_move = INCORPOREAL_MOVE_JAUNT
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
maxHealth = INFINITY
layer = GHOST_LAYER
healable = FALSE
spacewalk = TRUE
sight = SEE_SELF
throwforce = 0
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
response_help = "passes through"
response_disarm = "swings through"
response_harm = "punches through"
unsuitable_atmos_damage = 0
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) //I don't know how you'd apply those, but revenants no-sell them anyway.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = INFINITY
harm_intent_damage = 0
friendly = "touches"
status_flags = 0
wander = FALSE
density = FALSE
movement_type = FLYING
anchored = TRUE
mob_size = MOB_SIZE_TINY
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
speed = 1
unique_name = TRUE
hud_possible = list(ANTAG_HUD)
hud_type = /datum/hud/revenant
var/essence = 75 //The resource, and health, of revenants.
var/essence_regen_cap = 75 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
var/essence_regenerating = TRUE //If the revenant regenerates essence or not
var/essence_regen_amount = 5 //How much essence regenerates
var/essence_accumulated = 0 //How much essence the revenant has stolen
var/revealed = FALSE //If the revenant can take damage from normal sources.
var/unreveal_time = 0 //How long the revenant is revealed for, is about 2 seconds times this var.
var/unstun_time = 0 //How long the revenant is stunned for, is about 2 seconds times this var.
var/inhibited = FALSE //If the revenant's abilities are blocked by a chaplain's power.
var/essence_drained = 0 //How much essence the revenant will drain from the corpse it's feasting on.
var/draining = FALSE //If the revenant is draining someone.
var/list/drained_mobs = list() //Cannot harvest the same mob twice
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has. //TODO, add objective for getting a perfect soul(s?)
var/generated_objectives_and_spells = FALSE
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction(null))
random_revenant_name()
/mob/living/simple_animal/revenant/proc/random_revenant_name()
var/built_name = ""
built_name += pick(strings(REVENANT_NAME_FILE, "spirit_type"))
built_name += " of "
built_name += pick(strings(REVENANT_NAME_FILE, "adverb"))
built_name += pick(strings(REVENANT_NAME_FILE, "theme"))
name = built_name
/mob/living/simple_animal/revenant/Login()
..()
to_chat(src, "<span class='deadsay'><span class='big bold'>You are a revenant.</span></span>")
to_chat(src, "<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>")
to_chat(src, "<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>")
to_chat(src, "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>")
to_chat(src, "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>")
to_chat(src, "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>")
to_chat(src, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Revenant\">the wiki page</a> to learn more.</b>")
if(!generated_objectives_and_spells)
generated_objectives_and_spells = TRUE
mind.assigned_role = ROLE_REVENANT
mind.special_role = ROLE_REVENANT
SEND_SOUND(src, sound('sound/effects/ghost.ogg'))
mind.add_antag_datum(/datum/antagonist/revenant)
//Life, Stat, Hud Updates, and Say
/mob/living/simple_animal/revenant/Life()
if(stasis)
return
if(revealed && essence <= 0)
death()
if(unreveal_time && world.time >= unreveal_time)
unreveal_time = 0
revealed = FALSE
incorporeal_move = INCORPOREAL_MOVE_JAUNT
invisibility = INVISIBILITY_REVENANT
to_chat(src, "<span class='revenboldnotice'>You are once more concealed.</span>")
if(unstun_time && world.time >= unstun_time)
unstun_time = 0
notransform = FALSE
to_chat(src, "<span class='revenboldnotice'>You can move again!</span>")
if(essence_regenerating && !inhibited && essence < essence_regen_cap) //While inhibited, essence will not regenerate
essence = min(essence_regen_cap, essence+essence_regen_amount)
update_action_buttons_icon() //because we update something required by our spells in life, we need to update our buttons
update_spooky_icon()
update_health_hud()
..()
/mob/living/simple_animal/revenant/Stat()
..()
if(statpanel("Status"))
stat(null, "Current essence: [essence]/[essence_regen_cap]E")
stat(null, "Stolen essence: [essence_accumulated]E")
stat(null, "Stolen perfect souls: [perfectsouls]")
/mob/living/simple_animal/revenant/update_health_hud()
if(hud_used)
var/essencecolor = "#8F48C6"
if(essence > essence_regen_cap)
essencecolor = "#9A5ACB" //oh boy you've got a lot of essence
else if(!essence)
essencecolor = "#1D2953" //oh jeez you're dying
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='[essencecolor]'>[essence]E</font></div>"
/mob/living/simple_animal/revenant/med_hud_set_health()
return //we use no hud
/mob/living/simple_animal/revenant/med_hud_set_status()
return //we use no hud
/mob/living/simple_animal/revenant/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(!message)
return
src.log_talk(message, LOG_SAY)
var/rendered = "<span class='revennotice'><b>[src]</b> says, \"[message]\"</span>"
for(var/mob/M in GLOB.mob_list)
if(isrevenant(M))
to_chat(M, rendered)
else if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [rendered]")
return
//Immunities
/mob/living/simple_animal/revenant/ex_act(severity, target)
return 1 //Immune to the effects of explosions.
/mob/living/simple_animal/revenant/blob_act(obj/structure/blob/B)
return //blah blah blobs aren't in tune with the spirit world, or something.
/mob/living/simple_animal/revenant/singularity_act()
return //don't walk into the singularity expecting to find corpses, okay?
/mob/living/simple_animal/revenant/narsie_act()
return //most humans will now be either bones or harvesters, but we're still un-alive.
/mob/living/simple_animal/revenant/ratvar_act()
return //clocks get out reee
//damage, gibbing, and dying
/mob/living/simple_animal/revenant/attackby(obj/item/W, mob/living/user, params)
. = ..()
if(istype(W, /obj/item/nullrod))
visible_message("<span class='warning'>[src] violently flinches!</span>", \
"<span class='revendanger'>As \the [W] passes through you, you feel your essence draining away!</span>")
adjustBruteLoss(25) //hella effective
inhibited = TRUE
update_action_buttons_icon()
addtimer(CALLBACK(src, .proc/reset_inhibit), 30)
/mob/living/simple_animal/revenant/proc/reset_inhibit()
inhibited = FALSE
update_action_buttons_icon()
/mob/living/simple_animal/revenant/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(!forced && !revealed)
return FALSE
. = amount
essence = max(0, essence-amount)
if(updating_health)
update_health_hud()
if(!essence)
death()
/mob/living/simple_animal/revenant/dust(just_ash, drop_items, force)
death()
/mob/living/simple_animal/revenant/gib()
death()
/mob/living/simple_animal/revenant/death()
if(!revealed || stasis) //Revenants cannot die if they aren't revealed //or are already dead
return 0
stasis = TRUE
to_chat(src, "<span class='revendanger'>NO! No... it's too late, you can feel your essence [pick("breaking apart", "drifting away")]...</span>")
notransform = TRUE
revealed = TRUE
invisibility = 0
playsound(src, 'sound/effects/screech.ogg', 100, 1)
visible_message("<span class='warning'>[src] lets out a waning screech as violet mist swirls around its dissolving body!</span>")
icon_state = "revenant_draining"
for(var/i = alpha, i > 0, i -= 10)
stoplag()
alpha = i
visible_message("<span class='danger'>[src]'s body breaks apart into a fine pile of blue dust.</span>")
var/reforming_essence = essence_regen_cap //retain the gained essence capacity
var/obj/item/ectoplasm/revenant/R = new(get_turf(src))
R.essence = max(reforming_essence - 15 * perfectsouls, 75) //minus any perfect souls
R.old_key = client.key //If the essence reforms, the old revenant is put back in the body
R.revenant = src
invisibility = INVISIBILITY_ABSTRACT
revealed = FALSE
ghostize(0)//Don't re-enter invisible corpse
//reveal, stun, icon updates, cast checks, and essence changing
/mob/living/simple_animal/revenant/proc/reveal(time)
if(!src)
return
if(time <= 0)
return
revealed = TRUE
invisibility = 0
incorporeal_move = FALSE
if(!unreveal_time)
to_chat(src, "<span class='revendanger'>You have been revealed!</span>")
unreveal_time = world.time + time
else
to_chat(src, "<span class='revenwarning'>You have been revealed!</span>")
unreveal_time = unreveal_time + time
update_spooky_icon()
/mob/living/simple_animal/revenant/proc/stun(time)
if(!src)
return
if(time <= 0)
return
notransform = TRUE
if(!unstun_time)
to_chat(src, "<span class='revendanger'>You cannot move!</span>")
unstun_time = world.time + time
else
to_chat(src, "<span class='revenwarning'>You cannot move!</span>")
unstun_time = unstun_time + time
update_spooky_icon()
/mob/living/simple_animal/revenant/proc/update_spooky_icon()
if(revealed)
if(notransform)
if(draining)
icon_state = icon_drain
else
icon_state = icon_stun
else
icon_state = icon_reveal
else
icon_state = icon_idle
/mob/living/simple_animal/revenant/proc/castcheck(essence_cost)
if(!src)
return
var/turf/T = get_turf(src)
if(isclosedturf(T))
to_chat(src, "<span class='revenwarning'>You cannot use abilities from inside of a wall.</span>")
return FALSE
for(var/obj/O in T)
if(O.density && !O.CanPass(src, T))
to_chat(src, "<span class='revenwarning'>You cannot use abilities inside of a dense object.</span>")
return FALSE
if(inhibited)
to_chat(src, "<span class='revenwarning'>Your powers have been suppressed by nulling energy!</span>")
return FALSE
if(!change_essence_amount(essence_cost, TRUE))
to_chat(src, "<span class='revenwarning'>You lack the essence to use that ability.</span>")
return FALSE
return TRUE
/mob/living/simple_animal/revenant/proc/change_essence_amount(essence_amt, silent = FALSE, source = null)
if(!src)
return
if(essence + essence_amt <= 0)
return
essence = max(0, essence+essence_amt)
update_action_buttons_icon()
update_health_hud()
if(essence_amt > 0)
essence_accumulated = max(0, essence_accumulated+essence_amt)
if(!silent)
if(essence_amt > 0)
to_chat(src, "<span class='revennotice'>Gained [essence_amt]E[source ? " from [source]":""].</span>")
else
to_chat(src, "<span class='revenminor'>Lost [essence_amt]E[source ? " from [source]":""].</span>")
return 1
/mob/living/simple_animal/revenant/proc/death_reset()
revealed = FALSE
unreveal_time = 0
notransform = 0
unstun_time = 0
inhibited = FALSE
draining = FALSE
incorporeal_move = INCORPOREAL_MOVE_JAUNT
invisibility = INVISIBILITY_REVENANT
alpha=255
stasis = FALSE
//reforming
/obj/item/ectoplasm/revenant
name = "glimmering residue"
desc = "A pile of fine blue dust. Small tendrils of violet mist swirl around it."
icon = 'icons/effects/effects.dmi'
icon_state = "revenantEctoplasm"
w_class = WEIGHT_CLASS_SMALL
var/essence = 75 //the maximum essence of the reforming revenant
var/reforming = TRUE
var/inert = FALSE
var/old_key //key of the previous revenant, will have first pick on reform.
var/mob/living/simple_animal/revenant/revenant
/obj/item/ectoplasm/revenant/New()
..()
addtimer(CALLBACK(src, .proc/try_reform), 600)
/obj/item/ectoplasm/revenant/proc/scatter()
qdel(src)
/obj/item/ectoplasm/revenant/proc/try_reform()
if(reforming)
reforming = FALSE
reform()
else
inert = TRUE
visible_message("<span class='warning'>[src] settles down and seems lifeless.</span>")
/obj/item/ectoplasm/revenant/attack_self(mob/user)
if(!reforming || inert)
return ..()
user.visible_message("<span class='notice'>[user] scatters [src] in all directions.</span>", \
"<span class='notice'>You scatter [src] across the area. The particles slowly fade away.</span>")
user.dropItemToGround(src)
scatter()
/obj/item/ectoplasm/revenant/throw_impact(atom/hit_atom)
..()
if(inert)
return
visible_message("<span class='notice'>[src] breaks into particles upon impact, which fade away to nothingness.</span>")
scatter()
/obj/item/ectoplasm/revenant/examine(mob/user)
..()
if(inert)
to_chat(user, "<span class='revennotice'>It seems inert.</span>")
else if(reforming)
to_chat(user, "<span class='revenwarning'>It is shifting and distorted. It would be wise to destroy this.</span>")
/obj/item/ectoplasm/revenant/proc/reform()
if(QDELETED(src) || QDELETED(revenant) || inert)
return
var/key_of_revenant = FALSE
message_admins("Revenant ectoplasm was left undestroyed for 1 minute and is reforming into a new revenant.")
forceMove(drop_location()) //In case it's in a backpack or someone's hand
revenant.forceMove(loc)
if(old_key)
for(var/mob/M in GLOB.dead_mob_list)
if(M.client && M.client.key == old_key) //Only recreates the mob if the mob the client is in is dead
M.transfer_ckey(revenant.key, FALSE)
key_of_revenant = TRUE
break
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, null, ROLE_REVENANT, 50, revenant)
if(!LAZYLEN(candidates))
qdel(revenant)
message_admins("No candidates were found for the new revenant. Oh well!")
inert = TRUE
visible_message("<span class='revenwarning'>[src] settles down and seems lifeless.</span>")
return
var/mob/dead/observer/C = pick(candidates)
C.transfer_ckey(revenant.key, FALSE)
if(!revenant.key)
qdel(revenant)
message_admins("No ckey was found for the new revenant. Oh well!")
inert = TRUE
visible_message("<span class='revenwarning'>[src] settles down and seems lifeless.</span>")
return
message_admins("[key_of_revenant] has been [old_key == revenant.key ? "re":""]made into a revenant by reforming ectoplasm.")
log_game("[key_of_revenant] was [old_key == revenant.key ? "re":""]made as a revenant by reforming ectoplasm.")
visible_message("<span class='revenboldnotice'>[src] suddenly rises into the air before fading away.</span>")
revenant.essence = essence
revenant.essence_regen_cap = essence
revenant.death_reset()
revenant = null
qdel(src)
/obj/item/ectoplasm/revenant/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the shadow realm!</span>")
scatter()
return (OXYLOSS)
/obj/item/ectoplasm/revenant/Destroy()
if(!QDELETED(revenant))
qdel(revenant)
..()
//objectives
/datum/objective/revenant
var/targetAmount = 100
/datum/objective/revenant/New()
targetAmount = rand(350,600)
explanation_text = "Absorb [targetAmount] points of essence from humans."
..()
/datum/objective/revenant/check_completion()
if(!isrevenant(owner.current))
return FALSE
var/mob/living/simple_animal/revenant/R = owner.current
if(!R || R.stat == DEAD)
return FALSE
var/essence_stolen = R.essence_accumulated
if(essence_stolen < targetAmount)
return FALSE
return TRUE
/datum/objective/revenantFluff
/datum/objective/revenantFluff/New()
var/list/explanationTexts = list("Assist and exacerbate existing threats at critical moments.", \
"Avoid killing in plain sight.", \
"Cause as much chaos and anger as you can without being killed.", \
"Damage and render as much of the station rusted and unusable as possible.", \
"Disable and cause malfunctions in as many machines as possible.", \
"Ensure that any holy weapons are rendered unusable.", \
"Hinder the crew while attempting to avoid being noticed.", \
"Make the crew as miserable as possible.", \
"Make the clown as miserable as possible.", \
"Make the captain as miserable as possible.", \
"Prevent the use of energy weapons where possible.")
explanation_text = pick(explanationTexts)
..()
/datum/objective/revenantFluff/check_completion()
return TRUE
@@ -1,371 +0,0 @@
/mob/living/simple_animal/revenant/ClickOn(atom/A, params) //revenants can't interact with the world directly.
var/list/modifiers = params2list(params)
if(modifiers["shift"])
ShiftClickOn(A)
return
if(modifiers["alt"])
AltClickNoInteract(src, A)
return
if(ishuman(A))
if(A in drained_mobs)
to_chat(src, "<span class='revenwarning'>[A]'s soul is dead and empty.</span>" )
else if(in_range(src, A))
Harvest(A)
//Harvest; activated ly clicking the target, will try to drain their essence.
/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
if(!castcheck(0))
return
if(draining)
to_chat(src, "<span class='revenwarning'>You are already siphoning the essence of a soul!</span>")
return
if(!target.stat)
to_chat(src, "<span class='revennotice'>[target.p_their(TRUE)] soul is too strong to harvest.</span>")
if(prob(10))
to_chat(target, "You feel as if you are being watched.")
return
face_atom(target)
draining = TRUE
essence_drained += rand(15, 20)
to_chat(src, "<span class='revennotice'>You search for the soul of [target].</span>")
if(do_after(src, rand(10, 20), 0, target)) //did they get deleted in that second?
if(target.ckey)
to_chat(src, "<span class='revennotice'>[target.p_their(TRUE)] soul burns with intelligence.</span>")
essence_drained += rand(20, 30)
if(target.stat != DEAD)
to_chat(src, "<span class='revennotice'>[target.p_their(TRUE)] soul blazes with life!</span>")
essence_drained += rand(40, 50)
else
to_chat(src, "<span class='revennotice'>[target.p_their(TRUE)] soul is weak and faltering.</span>")
if(do_after(src, rand(15, 20), 0, target)) //did they get deleted NOW?
switch(essence_drained)
if(1 to 30)
to_chat(src, "<span class='revennotice'>[target] will not yield much essence. Still, every bit counts.</span>")
if(30 to 70)
to_chat(src, "<span class='revennotice'>[target] will yield an average amount of essence.</span>")
if(70 to 90)
to_chat(src, "<span class='revenboldnotice'>Such a feast! [target] will yield much essence to you.</span>")
if(90 to INFINITY)
to_chat(src, "<span class='revenbignotice'>Ah, the perfect soul. [target] will yield massive amounts of essence to you.</span>")
if(do_after(src, rand(15, 25), 0, target)) //how about now
if(!target.stat)
to_chat(src, "<span class='revenwarning'>[target.p_theyre(TRUE)] now powerful enough to fight off your draining.</span>")
to_chat(target, "<span class='boldannounce'>You feel something tugging across your body before subsiding.</span>")
draining = 0
essence_drained = 0
return //hey, wait a minute...
to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
if(target.stat != DEAD)
to_chat(target, "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>")
reveal(46)
stun(46)
target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, [target.p_their()] skin turning an ashy gray.</span>")
if(target.anti_magic_check(FALSE, TRUE))
to_chat(src, "<span class='revenminor'>Something's wrong! [target] seems to be resisting the siphoning, leaving you vulnerable!</span>")
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
"<span class='revenwarning'>Violet lights, dancing in your vision, receding--</span>")
draining = FALSE
return
var/datum/beam/B = Beam(target,icon_state="drain_life",time=INFINITY)
if(do_after(src, 46, 0, target)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
change_essence_amount(essence_drained, FALSE, target)
if(essence_drained <= 90 && target.stat != DEAD)
essence_regen_cap += 5
to_chat(src, "<span class='revenboldnotice'>The absorption of [target]'s living soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap].</span>")
if(essence_drained > 90)
essence_regen_cap += 15
perfectsouls++
to_chat(src, "<span class='revenboldnotice'>The perfection of [target]'s soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap].</span>")
to_chat(src, "<span class='revennotice'>[target]'s soul has been considerably weakened and will yield no more essence for the time being.</span>")
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
"<span class='revenwarning'>Violets lights, dancing in your vision, getting clo--</span>")
drained_mobs.Add(target)
target.death(0)
else
to_chat(src, "<span class='revenwarning'>[target ? "[target] has":"[target.p_theyve(TRUE)]"] been drawn out of your grasp. The link has been broken.</span>")
if(target) //Wait, target is WHERE NOW?
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
"<span class='revenwarning'>Violets lights, dancing in your vision, receding--</span>")
qdel(B)
else
to_chat(src, "<span class='revenwarning'>You are not close enough to siphon [target ? "[target]'s":"[target.p_their()]"] soul. The link has been broken.</span>")
draining = FALSE
essence_drained = 0
//Toggle night vision: lets the revenant toggle its night vision
/obj/effect/proc_holder/spell/targeted/night_vision/revenant
charge_max = 0
panel = "Revenant Abilities"
message = "<span class='revennotice'>You toggle your night vision.</span>"
action_icon = 'icons/mob/actions/actions_revenant.dmi'
action_icon_state = "r_nightvision"
action_background_icon_state = "bg_revenant"
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
/obj/effect/proc_holder/spell/targeted/telepathy/revenant
name = "Revenant Transmit"
panel = "Revenant Abilities"
action_icon = 'icons/mob/actions/actions_revenant.dmi'
action_icon_state = "r_transmit"
action_background_icon_state = "bg_revenant"
notice = "revennotice"
boldnotice = "revenboldnotice"
holy_check = TRUE
tinfoil_check = FALSE
/obj/effect/proc_holder/spell/aoe_turf/revenant
clothes_req = 0
action_icon = 'icons/mob/actions/actions_revenant.dmi'
action_background_icon_state = "bg_revenant"
panel = "Revenant Abilities (Locked)"
name = "Report this to a coder"
var/reveal = 80 //How long it reveals the revenant in deciseconds
var/stun = 20 //How long it stuns the revenant in deciseconds
var/locked = TRUE //If it's locked and needs to be unlocked before use
var/unlock_amount = 100 //How much essence it costs to unlock
var/cast_amount = 50 //How much essence it costs to use
/obj/effect/proc_holder/spell/aoe_turf/revenant/New()
..()
if(locked)
name = "[initial(name)] ([unlock_amount]E)"
else
name = "[initial(name)] ([cast_amount]E)"
/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr)
if(charge_counter < charge_max)
return FALSE
if(!istype(user)) //Badmins, no. Badmins, don't do it.
return TRUE
if(user.inhibited)
return FALSE
if(locked)
if(user.essence <= unlock_amount)
return FALSE
if(user.essence <= cast_amount)
return FALSE
return TRUE
/obj/effect/proc_holder/spell/aoe_turf/revenant/proc/attempt_cast(mob/living/simple_animal/revenant/user = usr)
if(!istype(user)) //If you're not a revenant, it works. Please, please, please don't give this to a non-revenant.
name = "[initial(name)]"
if(locked)
panel = "Revenant Abilities"
locked = FALSE
return TRUE
if(locked)
if(!user.castcheck(-unlock_amount))
charge_counter = charge_max
return FALSE
name = "[initial(name)] ([cast_amount]E)"
to_chat(user, "<span class='revennotice'>You have unlocked [initial(name)]!</span>")
panel = "Revenant Abilities"
locked = FALSE
charge_counter = charge_max
return FALSE
if(!user.castcheck(-cast_amount))
charge_counter = charge_max
return FALSE
name = "[initial(name)] ([cast_amount]E)"
user.reveal(reveal)
user.stun(stun)
if(action)
action.UpdateButtonIcon()
return TRUE
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload
name = "Overload Lights"
desc = "Directs a large amount of essence into nearby electrical lights, causing lights to shock those nearby."
charge_max = 200
range = 5
stun = 30
cast_amount = 40
var/shock_range = 2
var/shock_damage = 15
action_icon_state = "overload_lights"
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
INVOKE_ASYNC(src, .proc/overload, T, user)
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload(turf/T, mob/user)
for(var/obj/machinery/light/L in T)
if(!L.on)
return
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(4, 0, L)
s.start()
new /obj/effect/temp_visual/revenant(get_turf(L))
addtimer(CALLBACK(src, .proc/overload_shock, L, user), 20)
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload_shock(obj/machinery/light/L, mob/user)
if(!L.on) //wait, wait, don't shock me
return
flick("[L.base_state]2", L)
for(var/mob/living/carbon/human/M in view(shock_range, L))
if(M == user)
continue
L.Beam(M,icon_state="purple_lightning",time=5)
if(!M.anti_magic_check(FALSE, TRUE))
M.electrocute_act(shock_damage, L, safety=TRUE)
do_sparks(4, FALSE, M)
playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
//Defile: Corrupts nearby stuff, unblesses floor tiles.
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile
name = "Defile"
desc = "Twists and corrupts the nearby area as well as dispelling holy auras on floors."
charge_max = 150
range = 4
stun = 20
reveal = 40
unlock_amount = 75
cast_amount = 30
action_icon_state = "defile"
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
INVOKE_ASYNC(src, .proc/defile, T)
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile/proc/defile(turf/T)
for(var/obj/effect/blessing/B in T)
qdel(B)
new /obj/effect/temp_visual/revenant(T)
if(!isplatingturf(T) && !istype(T, /turf/open/floor/engine/cult) && isfloorturf(T) && prob(15))
var/turf/open/floor/floor = T
if(floor.intact && floor.floor_tile)
new floor.floor_tile(floor)
floor.broken = 0
floor.burnt = 0
floor.make_plating(1)
if(T.type == /turf/closed/wall && prob(15))
new /obj/effect/temp_visual/revenant(T)
T.ChangeTurf(/turf/closed/wall/rust)
if(T.type == /turf/closed/wall/r_wall && prob(10))
new /obj/effect/temp_visual/revenant(T)
T.ChangeTurf(/turf/closed/wall/r_wall/rust)
for(var/obj/effect/decal/cleanable/salt/salt in T)
new /obj/effect/temp_visual/revenant(T)
qdel(salt)
for(var/obj/structure/closet/closet in T.contents)
closet.open()
for(var/obj/structure/bodycontainer/corpseholder in T)
if(corpseholder.connected.loc == corpseholder)
corpseholder.open()
for(var/obj/machinery/dna_scannernew/dna in T)
dna.open_machine()
for(var/obj/structure/window/window in T)
window.take_damage(rand(30,80))
if(window && window.fulltile)
new /obj/effect/temp_visual/revenant/cracks(window.loc)
for(var/obj/machinery/light/light in T)
light.flicker(20) //spooky
//Malfunction: Makes bad stuff happen to robots and machines.
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction
name = "Malfunction"
desc = "Corrupts and damages nearby machines and mechanical objects."
charge_max = 200
range = 4
cast_amount = 60
unlock_amount = 200
action_icon_state = "malfunction"
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and everyone will hate you.
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
INVOKE_ASYNC(src, .proc/malfunction, T, user)
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction/proc/malfunction(turf/T, mob/user)
for(var/mob/living/simple_animal/bot/bot in T)
if(!bot.emagged)
new /obj/effect/temp_visual/revenant(bot.loc)
bot.locked = FALSE
bot.open = TRUE
bot.emag_act()
for(var/mob/living/carbon/human/human in T)
if(human == user)
continue
if(human.anti_magic_check(FALSE, TRUE))
continue
to_chat(human, "<span class='revenwarning'>You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")].</span>")
new /obj/effect/temp_visual/revenant(human.loc)
human.emp_act(EMP_HEAVY)
for(var/obj/thing in T)
if(istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes)) //Doesn't work on SMES and APCs, to prevent kekkery
continue
if(prob(20))
if(prob(50))
new /obj/effect/temp_visual/revenant(thing.loc)
thing.emag_act(null)
else
if(!istype(thing, /obj/machinery/clonepod)) //I hate everything but mostly the fact there's no better way to do this without just not affecting it at all
thing.emp_act(EMP_HEAVY)
for(var/mob/living/silicon/robot/S in T) //Only works on cyborgs, not AI
playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
new /obj/effect/temp_visual/revenant(S.loc)
S.spark_system.start()
S.emp_act(EMP_HEAVY)
//Blight: Infects nearby humans and in general messes living stuff up.
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight
name = "Blight"
desc = "Causes nearby living things to waste away."
charge_max = 200
range = 3
cast_amount = 50
unlock_amount = 200
action_icon_state = "blight"
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
INVOKE_ASYNC(src, .proc/blight, T, user)
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/proc/blight(turf/T, mob/user)
for(var/mob/living/mob in T)
if(mob == user)
continue
if(mob.anti_magic_check(FALSE, TRUE))
continue
new /obj/effect/temp_visual/revenant(mob.loc)
if(iscarbon(mob))
if(ishuman(mob))
var/mob/living/carbon/human/H = mob
if(H.dna && H.dna.species)
H.dna.species.handle_hair(H,"#1d2953") //will be reset when blight is cured
var/blightfound = FALSE
for(var/datum/disease/revblight/blight in H.diseases)
blightfound = TRUE
if(blight.stage < 5)
blight.stage++
if(!blightfound)
H.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE)
to_chat(H, "<span class='revenminor'>You feel [pick("suddenly sick", "a surge of nausea", "like your skin is <i>wrong</i>")].</span>")
else
if(mob.reagents)
mob.reagents.add_reagent("plasma", 5)
else
mob.adjustToxLoss(5)
for(var/obj/structure/spacevine/vine in T) //Fucking with botanists, the ability.
vine.add_atom_colour("#823abb", TEMPORARY_COLOUR_PRIORITY)
new /obj/effect/temp_visual/revenant(vine.loc)
QDEL_IN(vine, 10)
for(var/obj/structure/glowshroom/shroom in T)
shroom.add_atom_colour("#823abb", TEMPORARY_COLOUR_PRIORITY)
new /obj/effect/temp_visual/revenant(shroom.loc)
QDEL_IN(shroom, 10)
for(var/obj/machinery/hydroponics/tray in T)
new /obj/effect/temp_visual/revenant(tray.loc)
tray.pestlevel = rand(8, 10)
tray.weedlevel = rand(8, 10)
tray.toxic = rand(45, 55)
@@ -1,19 +0,0 @@
/datum/antagonist/revenant
name = "Revenant"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
/datum/antagonist/revenant/greet()
owner.announce_objectives()
/datum/antagonist/revenant/proc/forge_objectives()
var/datum/objective/revenant/objective = new
objective.owner = owner
objectives += objective
var/datum/objective/revenantFluff/objective2 = new
objective2.owner = owner
objectives += objective2
/datum/antagonist/revenant/on_gain()
forge_objectives()
. = ..()
@@ -1,75 +0,0 @@
/datum/disease/revblight
name = "Unnatural Wasting"
max_stages = 5
stage_prob = 10
spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
cure_text = "Holy water or extensive rest."
spread_text = "A burst of unholy energy"
cures = list("holywater")
cure_chance = 50 //higher chance to cure, because revenants are assholes
agent = "Unholy Forces"
viable_mobtypes = list(/mob/living/carbon/human)
disease_flags = CURABLE
permeability_mod = 1
severity = DISEASE_SEVERITY_DANGEROUS
var/finalstage = FALSE //Because we're spawning off the cure in the final stage, we need to check if we've done the final stage's effects.
var/depression = FALSE
/datum/disease/revblight/cure()
if(affected_mob)
affected_mob.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#1d2953")
if(affected_mob.dna && affected_mob.dna.species)
affected_mob.dna.species.handle_mutant_bodyparts(affected_mob)
affected_mob.dna.species.handle_hair(affected_mob)
SEND_SIGNAL(affected_mob, COMSIG_CLEAR_MOOD_EVENT, "rev_blight")
..()
/datum/disease/revblight/stage_act()
if(!finalstage)
if(affected_mob.lying && prob(stage*4))
to_chat(affected_mob, "<span class='notice'>You feel better.</span>")
cure()
return
if(prob(stage*3))
to_chat(affected_mob, "<span class='revennotice'>You suddenly feel [pick("sick and tired", "disoriented", "tired and confused", "nauseated", "faint", "dizzy")]...</span>")
affected_mob.confused += 8
affected_mob.adjustStaminaLoss(8)
new /obj/effect/temp_visual/revenant(affected_mob.loc)
if(prob(45))
affected_mob.adjustStaminaLoss(stage)
..() //So we don't increase a stage before applying the stage damage.
switch(stage)
if(2)
if(prob(5))
affected_mob.emote("pale")
if(3)
if(!depression)
SEND_SIGNAL(affected_mob, COMSIG_ADD_MOOD_EVENT, "rev_blight", /datum/mood_event/revenant_blight)
depression = TRUE
SEND_SIGNAL(affected_mob, COMSIG_MODIFY_SANITY, -0.12, SANITY_CRAZY)
if(prob(10))
affected_mob.emote(pick("pale","shiver"))
if(4)
SEND_SIGNAL(affected_mob, COMSIG_MODIFY_SANITY, -0.18, SANITY_CRAZY)
if(prob(15))
affected_mob.emote(pick("pale","shiver","cries"))
if(5)
if(!finalstage)
finalstage = TRUE
to_chat(affected_mob, "<span class='revenbignotice'>You feel like [pick("nothing's worth it anymore", "nobody ever needed your help", "nothing you did mattered", "everything you tried to do was worthless")].</span>")
affected_mob.adjustStaminaLoss(45)
if(affected_mob.dna?.species)
affected_mob.dna.species.handle_mutant_bodyparts(affected_mob,"#1d2953")
affected_mob.dna.species.handle_hair(affected_mob,"#1d2953")
affected_mob.visible_message("<span class='warning'>[affected_mob] looks terrifyingly gaunt...</span>", "<span class='revennotice'>You suddenly feel like your skin is <i>wrong</i>...</span>")
affected_mob.add_atom_colour("#1d2953", TEMPORARY_COLOUR_PRIORITY)
new /obj/effect/temp_visual/revenant(affected_mob.loc)
addtimer(CALLBACK(src, .proc/curses), 150)
/datum/disease/revblight/proc/curses()
if(QDELETED(affected_mob))
return
affected_mob.playsound_local(affected_mob, 'sound/effects/curse5.ogg', 40, 1, -1)
to_chat(affected_mob, "<span class='revendanger'>You sense the terrific curse of a vengeful ghost befall upon you...</span>")
affected_mob.apply_necropolis_curse(null, 7 MINUTES) //Once the blight has done its course without being cured beforehand, it will cast a necrocurse to compensate how underpowered it's.
cure()
@@ -1,60 +0,0 @@
#define REVENANT_SPAWN_THRESHOLD 20
/datum/round_event_control/revenant
name = "Spawn Revenant" // Did you mean 'griefghost'?
typepath = /datum/round_event/ghost_role/revenant
weight = 7
gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 5
/datum/round_event/ghost_role/revenant
var/ignore_mobcheck = FALSE
role_name = "revenant"
/datum/round_event/ghost_role/revenant/New(my_processing = TRUE, new_ignore_mobcheck = FALSE)
..()
ignore_mobcheck = new_ignore_mobcheck
/datum/round_event/ghost_role/revenant/spawn_role()
if(!ignore_mobcheck)
var/deadMobs = 0
for(var/mob/M in GLOB.dead_mob_list)
deadMobs++
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
return WAITING_FOR_SOMETHING
var/list/candidates = get_candidates(ROLE_REVENANT, null, ROLE_REVENANT)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/observer/selected = pick_n_take(candidates)
var/list/spawn_locs = list()
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
var/turf/T = get_turf(L)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len || spawn_locs.len < 15) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from
for(var/obj/structure/bodycontainer/bc in GLOB.bodycontainers)
var/turf/T = get_turf(bc)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
if(isturf(L.loc))
spawn_locs += L.loc
if(!spawn_locs.len) //If we can't find either, just spawn the revenant at the player's location
spawn_locs += get_turf(selected)
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
return MAP_ERROR
var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
selected.transfer_ckey(revvie, FALSE)
message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by an event.")
log_game("[key_name(revvie)] was spawned as a revenant by an event.")
spawned_mobs += revvie
return SUCCESSFUL_SPAWN
@@ -1,408 +0,0 @@
//How often to check for promotion possibility
#define HEAD_UPDATE_PERIOD 300
/datum/antagonist/rev
name = "Revolutionary"
roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen
antagpanel_category = "Revolution"
job_rank = ROLE_REV
antag_moodlet = /datum/mood_event/revolution
var/hud_type = "rev"
var/datum/team/revolution/rev_team
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
. = ..()
if(.)
if(new_owner.assigned_role in GLOB.command_positions)
return FALSE
if(new_owner.unconvertable)
return FALSE
if(new_owner.current && HAS_TRAIT(new_owner.current, TRAIT_MINDSHIELD))
return FALSE
/datum/antagonist/rev/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_rev_icons_added(M)
/datum/antagonist/rev/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_rev_icons_removed(M)
/datum/antagonist/rev/proc/equip_rev()
return
/datum/antagonist/rev/on_gain()
. = ..()
create_objectives()
equip_rev()
owner.current.log_message("has been converted to the revolution!", LOG_ATTACK, color="red")
/datum/antagonist/rev/on_removal()
remove_objectives()
. = ..()
/datum/antagonist/rev/greet()
to_chat(owner, "<span class='userdanger'>You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!</span>")
owner.announce_objectives()
/datum/antagonist/rev/create_team(datum/team/revolution/new_team)
if(!new_team)
//For now only one revolution at a time
for(var/datum/antagonist/rev/head/H in GLOB.antagonists)
if(!H.owner)
continue
if(H.rev_team)
rev_team = H.rev_team
return
rev_team = new /datum/team/revolution
rev_team.update_objectives()
rev_team.update_heads()
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
rev_team = new_team
/datum/antagonist/rev/get_team()
return rev_team
/datum/antagonist/rev/proc/create_objectives()
objectives |= rev_team.objectives
/datum/antagonist/rev/proc/remove_objectives()
objectives -= rev_team.objectives
//Bump up to head_rev
/datum/antagonist/rev/proc/promote()
var/old_team = rev_team
var/datum/mind/old_owner = owner
silent = TRUE
owner.remove_antag_datum(/datum/antagonist/rev)
var/datum/antagonist/rev/head/new_revhead = new()
new_revhead.silent = TRUE
old_owner.add_antag_datum(new_revhead,old_team)
new_revhead.silent = FALSE
to_chat(old_owner, "<span class='userdanger'>You have proved your devotion to revolution! You are a head revolutionary now!</span>")
/datum/antagonist/rev/get_admin_commands()
. = ..()
.["Promote"] = CALLBACK(src,.proc/admin_promote)
/datum/antagonist/rev/proc/admin_promote(mob/admin)
var/datum/mind/O = owner
promote()
message_admins("[key_name_admin(admin)] has head-rev'ed [O].")
log_admin("[key_name(admin)] has head-rev'ed [O].")
/datum/antagonist/rev/head/admin_add(datum/mind/new_owner,mob/admin)
give_flash = TRUE
give_hud = TRUE
remove_clumsy = TRUE
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has head-rev'ed [new_owner.current].")
log_admin("[key_name(admin)] has head-rev'ed [new_owner.current].")
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the revolutionaries' leadership now!</span>")
/datum/antagonist/rev/head/get_admin_commands()
. = ..()
. -= "Promote"
.["Take flash"] = CALLBACK(src,.proc/admin_take_flash)
.["Give flash"] = CALLBACK(src,.proc/admin_give_flash)
.["Repair flash"] = CALLBACK(src,.proc/admin_repair_flash)
.["Demote"] = CALLBACK(src,.proc/admin_demote)
/datum/antagonist/rev/head/proc/admin_take_flash(mob/admin)
var/list/L = owner.current.get_contents()
var/obj/item/assembly/flash/flash = locate() in L
if (!flash)
to_chat(admin, "<span class='danger'>Deleting flash failed!</span>")
return
qdel(flash)
/datum/antagonist/rev/head/proc/admin_give_flash(mob/admin)
//This is probably overkill but making these impact state annoys me
var/old_give_flash = give_flash
var/old_give_hud = give_hud
var/old_remove_clumsy = remove_clumsy
give_flash = TRUE
give_hud = FALSE
remove_clumsy = FALSE
equip_rev()
give_flash = old_give_flash
give_hud = old_give_hud
remove_clumsy = old_remove_clumsy
/datum/antagonist/rev/head/proc/admin_repair_flash(mob/admin)
var/list/L = owner.current.get_contents()
var/obj/item/assembly/flash/flash = locate() in L
if (!flash)
to_chat(admin, "<span class='danger'>Repairing flash failed!</span>")
else
flash.crit_fail = 0
flash.update_icon()
/datum/antagonist/rev/head/proc/admin_demote(datum/mind/target,mob/user)
message_admins("[key_name_admin(user)] has demoted [owner.current] from head revolutionary.")
log_admin("[key_name(user)] has demoted [owner.current] from head revolutionary.")
demote()
/datum/antagonist/rev/head
name = "Head Revolutionary"
hud_type = "rev_head"
var/remove_clumsy = FALSE
var/give_flash = FALSE
var/give_hud = TRUE
/datum/antagonist/rev/head/antag_listing_name()
return ..() + "(Leader)"
/datum/antagonist/rev/proc/update_rev_icons_added(mob/living/M)
var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV]
revhud.join_hud(M)
set_antag_hud(M,hud_type)
/datum/antagonist/rev/proc/update_rev_icons_removed(mob/living/M)
var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV]
revhud.leave_hud(M)
set_antag_hud(M, null)
/datum/antagonist/rev/proc/can_be_converted(mob/living/candidate)
if(!candidate.mind)
return FALSE
if(!can_be_owned(candidate.mind))
return FALSE
var/mob/living/carbon/C = candidate //Check to see if the potential rev is implanted
if(!istype(C)) //Can't convert simple animals
return FALSE
return TRUE
/datum/antagonist/rev/proc/add_revolutionary(datum/mind/rev_mind,stun = TRUE)
if(!can_be_converted(rev_mind.current))
return FALSE
if(stun)
if(iscarbon(rev_mind.current))
var/mob/living/carbon/carbon_mob = rev_mind.current
carbon_mob.silent = max(carbon_mob.silent, 5)
carbon_mob.flash_act(1, 1)
rev_mind.current.Stun(100)
rev_mind.add_antag_datum(/datum/antagonist/rev,rev_team)
rev_mind.special_role = ROLE_REV
return TRUE
/datum/antagonist/rev/head/proc/demote()
var/datum/mind/old_owner = owner
var/old_team = rev_team
silent = TRUE
owner.remove_antag_datum(/datum/antagonist/rev/head)
var/datum/antagonist/rev/new_rev = new /datum/antagonist/rev()
new_rev.silent = TRUE
old_owner.add_antag_datum(new_rev,old_team)
new_rev.silent = FALSE
to_chat(old_owner, "<span class='userdanger'>Revolution has been disappointed of your leader traits! You are a regular revolutionary now!</span>")
/datum/antagonist/rev/farewell()
if(ishuman(owner.current))
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel... You don't seem to be able to recall the names of your comrades, not even your leaders...</span>")
else if(issilicon(owner.current))
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing of your time spent reprogrammed, you can't even remember the names or identities of anyone involved...</span>")
/datum/antagonist/rev/head/farewell()
if((ishuman(owner.current) || ismonkey(owner.current)))
if(owner.current.stat != DEAD)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class ='deconversion_message bold'>You have given up your cause of overthrowing the command staff. You are no longer a Head Revolutionary.</span>")
else
to_chat(owner, "<span class ='deconversion_message bold'>The sweet release of death. You are no longer a Head Revolutionary.</span>")
else if(issilicon(owner.current))
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, suppressing the disloyal personality traits from the MMI before initalizing it.</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and suppresses your unwanted personality traits! You feel more content with the leadership around these parts.</span>")
//blunt trauma deconversions call this through species.dm spec_attacked_by()
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
log_attack("[key_name(owner.current)] has been deconverted from the revolution by [ismob(deconverter) ? key_name(deconverter) : deconverter]!")
if(borged)
message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]")
owner.special_role = null
if(iscarbon(owner.current))
var/mob/living/carbon/C = owner.current
C.Unconscious(100)
owner.remove_antag_datum(type)
/datum/antagonist/rev/head/remove_revolutionary(borged,deconverter)
if(borged || deconverter == "gamemode")
. = ..()
/datum/antagonist/rev/head/equip_rev()
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
if(remove_clumsy && owner.assigned_role == "Clown")
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
H.dna.remove_mutation(CLOWNMUT)
if(give_flash)
var/obj/item/assembly/flash/T = new(H)
var/list/slots = list (
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/where = H.equip_in_one_of_slots(T, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you a flash.")
else
to_chat(H, "The flash in your [where] will help you to persuade the crew to join your cause.")
if(give_hud)
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = new(H)
S.Insert(H, special = FALSE, drop_if_replaced = FALSE)
to_chat(H, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
/datum/team/revolution
name = "Revolution"
var/max_headrevs = 3
var/list/ex_headrevs = list() // Dynamic removes revs on loss, used to keep a list for the roundend report.
var/list/ex_revs = list()
/datum/team/revolution/proc/update_objectives(initial = FALSE)
var/untracked_heads = SSjob.get_all_heads()
for(var/datum/objective/mutiny/O in objectives)
untracked_heads -= O.target
for(var/datum/mind/M in untracked_heads)
var/datum/objective/mutiny/new_target = new()
new_target.team = src
new_target.target = M
new_target.update_explanation_text()
objectives += new_target
for(var/datum/mind/M in members)
var/datum/antagonist/rev/R = M.has_antag_datum(/datum/antagonist/rev)
R.objectives |= objectives
addtimer(CALLBACK(src,.proc/update_objectives),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
/datum/team/revolution/proc/head_revolutionaries()
. = list()
for(var/datum/mind/M in members)
if(M.has_antag_datum(/datum/antagonist/rev/head))
. += M
/datum/team/revolution/proc/update_heads()
if(SSticker.HasRoundStarted())
var/list/datum/mind/head_revolutionaries = head_revolutionaries()
var/list/datum/mind/heads = SSjob.get_all_heads()
var/list/sec = SSjob.get_all_sec()
if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3)))
var/list/datum/mind/non_heads = members - head_revolutionaries
var/list/datum/mind/promotable = list()
for(var/datum/mind/khrushchev in non_heads)
if(khrushchev.current && !khrushchev.current.incapacitated() && !khrushchev.current.restrained() && khrushchev.current.client && khrushchev.current.stat != DEAD)
if(ROLE_REV in khrushchev.current.client.prefs.be_special)
promotable += khrushchev
if(promotable.len)
var/datum/mind/new_leader = pick(promotable)
var/datum/antagonist/rev/rev = new_leader.has_antag_datum(/datum/antagonist/rev)
rev.promote()
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
/datum/team/revolution/proc/save_members()
ex_headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE)
ex_revs = get_antag_minds(/datum/antagonist/rev, TRUE)
/datum/team/revolution/roundend_report()
if(!members.len && !ex_headrevs.len)
return
var/list/result = list()
result += "<div class='panel redborder'>"
var/num_revs = 0
var/num_survivors = 0
for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
if(survivor.ckey)
num_survivors++
if(survivor.mind)
if(is_revolutionary(survivor))
num_revs++
if(num_survivors)
result += "Command's Approval Rating: <B>[100 - round((num_revs/num_survivors)*100, 0.1)]%</B><br>"
var/list/targets = list()
var/list/datum/mind/headrevs
var/list/datum/mind/revs
if(ex_headrevs.len)
headrevs = ex_headrevs
else
headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE)
if(ex_revs.len)
revs = ex_revs
else
revs = get_antag_minds(/datum/antagonist/rev, TRUE)
if(headrevs.len)
var/list/headrev_part = list()
headrev_part += "<span class='header'>The head revolutionaries were:</span>"
headrev_part += printplayerlist(headrevs,TRUE)
result += headrev_part.Join("<br>")
if(revs.len)
var/list/rev_part = list()
rev_part += "<span class='header'>The revolutionaries were:</span>"
rev_part += printplayerlist(revs,TRUE)
result += rev_part.Join("<br>")
var/list/heads = SSjob.get_all_heads()
if(heads.len)
var/head_text = "<span class='header'>The heads of staff were:</span>"
head_text += "<ul class='playerlist'>"
for(var/datum/mind/head in heads)
var/target = (head in targets)
head_text += "<li>"
if(target)
head_text += "<span class='redtext'>Target</span>"
head_text += "[printplayer(head, 1)]</li>"
head_text += "</ul><br>"
result += head_text
result += "</div>"
return result.Join()
/datum/team/revolution/antag_listing_entry()
var/common_part = ""
var/list/parts = list()
parts += "<b>[antag_listing_name()]</b><br>"
parts += "<table cellspacing=5>"
var/list/heads = get_team_antags(/datum/antagonist/rev/head,TRUE)
for(var/datum/antagonist/A in heads | get_team_antags())
parts += A.antag_listing_entry()
parts += "</table>"
parts += antag_listing_footer()
common_part = parts.Join()
var/heads_report = "<b>Heads of Staff</b><br>"
heads_report += "<table cellspacing=5>"
for(var/datum/mind/N in SSjob.get_living_heads())
var/mob/M = N.current
if(M)
heads_report += "<tr><td><a href='?_src_=holder;[HrefToken()];adminplayeropts=[REF(M)]'>[M.real_name]</a>[M.client ? "" : " <i>(No Client)</i>"][M.stat == DEAD ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
heads_report += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
heads_report += "<td><A href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(M)]'>FLW</a></td>"
var/turf/mob_loc = get_turf(M)
heads_report += "<td>[mob_loc.loc]</td></tr>"
else
heads_report += "<tr><td><a href='?_src_=vars;[HrefToken()];Vars=[REF(N)]'>[N.name]([N.key])</a><i>Head body destroyed!</i></td>"
heads_report += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
heads_report += "</table>"
return common_part + heads_report
/datum/team/revolution/is_gamemode_hero()
return SSticker.mode.name == "revolution"
@@ -1,185 +0,0 @@
//////////////////The Monster
/mob/living/simple_animal/slaughter
name = "slaughter demon"
real_name = "slaughter demon"
desc = "A large, menacing creature covered in armored black scales."
speak_emote = list("gurgles")
emote_hear = list("wails","screeches")
response_help = "thinks better of touching"
response_disarm = "flails at"
response_harm = "punches"
icon = 'icons/mob/mob.dmi'
icon_state = "daemon"
icon_living = "daemon"
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
status_flags = CANPUSH
attack_sound = 'sound/magic/demon_attack1.ogg'
var/feast_sound = 'sound/magic/demon_consume.ogg'
death_sound = 'sound/magic/demon_dies.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = INFINITY
faction = list("slaughter")
attacktext = "wildly tears into"
maxHealth = 200
health = 200
healable = 0
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
obj_damage = 50
melee_damage_lower = 30
melee_damage_upper = 30
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
bloodcrawl = BLOODCRAWL_EAT
var/playstyle_string = "<span class='big bold'>You are a slaughter demon,</span><B> a terrible creature from another realm. You have a single desire: To kill. \
You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and disappearing from the station at will. \
Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. </B>"
loot = list(/obj/effect/decal/cleanable/blood, \
/obj/effect/decal/cleanable/blood/innards, \
/obj/item/organ/heart/demon)
del_on_death = 1
deathmessage = "screams in anger as it collapses into a puddle of viscera!"
/mob/living/simple_animal/slaughter/Initialize()
..()
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell)
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
bloodspell.phased = TRUE
/obj/effect/decal/cleanable/blood/innards
icon = 'icons/obj/surgery.dmi'
name = "pile of viscera"
desc = "A repulsive pile of guts and gore."
gender = NEUTER
icon_state = "innards"
random_icon_states = null
/mob/living/simple_animal/slaughter/phasein()
. = ..()
add_movespeed_modifier(MOVESPEED_ID_SLAUGHTER, update=TRUE, priority=100, multiplicative_slowdown=-1)
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, MOVESPEED_ID_SLAUGHTER, TRUE), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl
/obj/item/organ/heart/demon
name = "demon heart"
desc = "Still it beats furiously, emanating an aura of utter hate."
icon = 'icons/obj/surgery.dmi'
icon_state = "demon_heart-on"
decay_factor = 0
/obj/item/organ/heart/demon/update_icon()
return //always beating visually
/obj/item/organ/heart/demon/attack(mob/M, mob/living/carbon/user, obj/target)
if(M != user)
return ..()
user.visible_message("<span class='warning'>[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!</span>", \
"<span class='danger'>An unnatural hunger consumes you. You raise [src] your mouth and devour it!</span>")
playsound(user, 'sound/magic/demon_consume.ogg', 50, 1)
for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list)
if(knownspell.type == /obj/effect/proc_holder/spell/bloodcrawl)
to_chat(user, "<span class='warning'>...and you don't feel any different.</span>")
qdel(src)
return
user.visible_message("<span class='warning'>[user]'s eyes flare a deep crimson!</span>", \
"<span class='userdanger'>You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!</span>")
user.temporarilyRemoveItemFromInventory(src, TRUE)
src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E
/obj/item/organ/heart/demon/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
..()
if(M.mind)
M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null))
/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0)
..()
if(M.mind)
M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
/obj/item/organ/heart/demon/Stop()
return 0 // Always beating.
/mob/living/simple_animal/slaughter/laughter
// The laughter demon! It's everyone's best friend! It just wants to hug
// them so much, it wants to hug everyone at once!
name = "laughter demon"
real_name = "laughter demon"
desc = "A large, adorable creature covered in armor with pink bows."
speak_emote = list("giggles","titters","chuckles")
emote_hear = list("guffaws","laughs")
response_help = "hugs"
attacktext = "wildly tickles"
attack_sound = 'sound/items/bikehorn.ogg'
feast_sound = 'sound/spookoween/scary_horn2.ogg'
death_sound = 'sound/misc/sadtrombone.ogg'
icon_state = "bowmon"
icon_living = "bowmon"
deathmessage = "fades out, as all of its friends are released from its \
prison of hugs."
loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"})
// Keep the people we hug!
var/list/consumed_mobs = list()
playstyle_string = "<span class='big bold'>You are a laughter \
demon,</span><B> a wonderful creature from another realm. You have a single \
desire: <span class='clown'>To hug and tickle.</span><BR>\
You may use the \"Blood Crawl\" ability near blood pools to travel \
through them, appearing and disappearing from the station at will. \
Pulling a dead or unconscious mob while you enter a pool will pull \
them in with you, allowing you to hug them and regain your health.<BR> \
You move quickly upon leaving a pool of blood, but the material world \
will soon sap your strength and leave you sluggish.<BR>\
What makes you a little sad is that people seem to die when you tickle \
them; but don't worry! When you die, everyone you hugged will be \
released and fully healed, because in the end it's just a jape, \
sibling!</B>"
/mob/living/simple_animal/slaughter/laughter/Destroy()
release_friends()
. = ..()
/mob/living/simple_animal/slaughter/laughter/ex_act(severity)
switch(severity)
if(1)
death()
if(2)
adjustBruteLoss(60)
if(3)
adjustBruteLoss(30)
/mob/living/simple_animal/slaughter/laughter/proc/release_friends()
if(!consumed_mobs)
return
for(var/mob/living/M in consumed_mobs)
if(!M)
continue
var/turf/T = find_safe_turf()
if(!T)
T = get_turf(src)
M.forceMove(T)
if(M.revive(full_heal = TRUE, admin_revive = TRUE))
M.grab_ghost(force = TRUE)
playsound(T, feast_sound, 50, 1, -1)
to_chat(M, "<span class='clown'>You leave [src]'s warm embrace, and feel ready to take on the world.</span>")
/mob/living/simple_animal/slaughter/laughter/bloodcrawl_swallow(var/mob/living/victim)
if(consumed_mobs)
// Keep their corpse so rescue is possible
consumed_mobs += victim
else
// Be safe and just eject the corpse
victim.forceMove(get_turf(victim))
victim.exit_blood_effect()
victim.visible_message("[victim] falls out of the air, covered in blood, looking highly confused. And dead.")
@@ -1,31 +0,0 @@
/datum/antagonist/slaughter
name = "Slaughter demon"
show_name_in_check_antagonists = TRUE
var/objective_verb = "Kill"
var/datum/mind/summoner
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
/datum/antagonist/slaughter/on_gain()
forge_objectives()
. = ..()
/datum/antagonist/slaughter/greet()
. = ..()
owner.announce_objectives()
/datum/antagonist/slaughter/proc/forge_objectives()
if(summoner)
var/datum/objective/assassinate/new_objective = new /datum/objective/assassinate
new_objective.owner = owner
new_objective.target = summoner
new_objective.explanation_text = "[objective_verb] [summoner.name], the one who summoned you."
objectives += new_objective
var/datum/objective/new_objective2 = new /datum/objective
new_objective2.owner = owner
new_objective2.explanation_text = "[objective_verb] everyone[summoner ? " else while you're at it":""]."
objectives += new_objective2
/datum/antagonist/slaughter/laughter
name = "Laughter demon"
objective_verb = "Hug and Tickle"
@@ -1,48 +0,0 @@
/datum/round_event_control/slaughter
name = "Spawn Slaughter Demon"
typepath = /datum/round_event/ghost_role/slaughter
weight = 1 //Very rare
max_occurrences = 1
gamemode_blacklist = list("dynamic")
earliest_start = 1 HOURS
min_players = 20
/datum/round_event/ghost_role/slaughter
minimum_required = 1
role_name = "slaughter demon"
/datum/round_event/ghost_role/slaughter/spawn_role()
var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/selected = pick_n_take(candidates)
var/datum/mind/player_mind = new /datum/mind(selected.key)
player_mind.active = 1
var/list/spawn_locs = list()
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
if(isturf(L.loc))
spawn_locs += L.loc
if(!spawn_locs)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
var/obj/effect/dummy/phased_mob/slaughter/holder = new /obj/effect/dummy/phased_mob/slaughter((pick(spawn_locs)))
var/mob/living/simple_animal/slaughter/S = new (holder)
S.holder = holder
player_mind.transfer_to(S)
player_mind.assigned_role = "Slaughter Demon"
player_mind.special_role = "Slaughter Demon"
player_mind.add_antag_datum(/datum/antagonist/slaughter)
to_chat(S, S.playstyle_string)
to_chat(S, "<B>You are currently not currently in the same plane of existence as the station. Blood Crawl near a blood pool to manifest.</B>")
SEND_SOUND(S, 'sound/magic/demon_dies.ogg')
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a slaughter demon by an event.")
log_game("[key_name(S)] was spawned as a slaughter demon by an event.")
spawned_mobs += S
return SUCCESSFUL_SPAWN
@@ -1,38 +0,0 @@
/datum/antagonist/survivalist
name = "Survivalist"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
var/datum/objective/survive/survive = new
survive.owner = owner
objectives += survive
/datum/antagonist/survivalist/on_gain()
owner.special_role = "survivalist"
forge_objectives()
. = ..()
/datum/antagonist/survivalist/greet()
to_chat(owner, "<B>You are the survivalist![greet_message]</B>")
owner.announce_objectives()
/datum/antagonist/survivalist/guns
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/guns/forge_objectives()
var/datum/objective/steal_five_of_type/summon_guns/guns = new
guns.owner = owner
objectives += guns
..()
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
greet_message = "Grow your newfound talent! Grab as many magical artefacts as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/magic/forge_objectives()
var/datum/objective/steal_five_of_type/summon_magic/magic = new
magic.owner = owner
objectives += magic
..()
-680
View File
@@ -1,680 +0,0 @@
////Deactivated swarmer shell////
/obj/item/deactivated_swarmer
name = "deactivated swarmer"
desc = "A shell of swarmer that was completely powered down. It can no longer activate itself."
icon = 'icons/mob/swarmer.dmi'
icon_state = "swarmer_unactivated"
materials = list(MAT_METAL=10000, MAT_GLASS=4000)
/obj/effect/mob_spawn/swarmer
name = "unactivated swarmer"
desc = "A currently unactivated swarmer. Swarmers can self activate at any time, it would be wise to immediately dispose of this."
icon = 'icons/mob/swarmer.dmi'
icon_state = "swarmer_unactivated"
density = FALSE
anchored = FALSE
mob_type = /mob/living/simple_animal/hostile/swarmer
mob_name = "a swarmer"
job_description = "Swarmer"
death = FALSE
roundstart = FALSE
flavour_text = {"
<b>You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.</b>
<b>Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.</b>
<b>Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage.</b>
<b>Objectives:</b>
1. Consume resources and replicate until there are no more resources left.
2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable.
3. Biological resources will be harvested at a later date; do not harm them.
"}
/obj/effect/mob_spawn/swarmer/Initialize()
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
. = ..()
if(.)
return
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)
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>")
new /obj/item/deactivated_swarmer(get_turf(src))
qdel(src)
else
..()
////The Mob itself////
/mob/living/simple_animal/hostile/swarmer
name = "Swarmer"
unique_name = 1
icon = 'icons/mob/swarmer.dmi'
desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely."
speak_emote = list("tones")
initial_language_holder = /datum/language_holder/swarmer
bubble_icon = "swarmer"
mob_biotypes = list(MOB_ROBOTIC)
health = 40
maxHealth = 40
status_flags = CANPUSH
icon_state = "swarmer"
icon_living = "swarmer"
icon_dead = "swarmer_unactivated"
icon_gib = null
wander = 0
harm_intent_damage = 5
minbodytemp = 0
maxbodytemp = 500
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 0
melee_damage_lower = 15
melee_damage_upper = 15
melee_damage_type = STAMINA
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD)
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
attacktext = "shocks"
attack_sound = 'sound/effects/empulse.ogg'
friendly = "pinches"
speed = 0
faction = list("swarmer")
AIStatus = AI_OFF
pass_flags = PASSTABLE
mob_size = MOB_SIZE_TINY
ventcrawler = VENTCRAWLER_ALWAYS
ranged = 1
projectiletype = /obj/item/projectile/beam/disabler
ranged_cooldown_time = 20
projectilesound = 'sound/weapons/taser2.ogg'
loot = list(/obj/effect/decal/cleanable/robot_debris, /obj/item/stack/ore/bluespace_crystal)
del_on_death = 1
deathmessage = "explodes with a sharp pop!"
light_color = LIGHT_COLOR_CYAN
hud_type = /datum/hud/swarmer
speech_span = SPAN_ROBOT
var/resources = 0 //Resource points, generated by consuming metal/glass
var/max_resources = 100
/mob/living/simple_animal/hostile/swarmer/Initialize()
. = ..()
verbs -= /mob/living/verb/pulled
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/simple_animal/hostile/swarmer/med_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "hudstat"
/mob/living/simple_animal/hostile/swarmer/Stat()
..()
if(statpanel("Status"))
stat("Resources:",resources)
/mob/living/simple_animal/hostile/swarmer/emp_act()
. = ..()
if(. & EMP_PROTECT_SELF)
return
if(health > 1)
adjustHealth(health-1)
else
death()
/mob/living/simple_animal/hostile/swarmer/CanPass(atom/movable/O)
if(istype(O, /obj/item/projectile/beam/disabler))//Allows for swarmers to fight as a group without wasting their shots hitting each other
return 1
if(isswarmer(O))
return 1
..()
////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
/mob/living/simple_animal/hostile/swarmer/AttackingTarget()
if(!isliving(target))
return target.swarmer_act(src)
else
return ..()
/mob/living/simple_animal/hostile/swarmer/CtrlClickOn(atom/A)
face_atom(A)
if(!isturf(loc))
return
if(next_move > world.time)
return
if(!A.Adjacent(src))
return
A.swarmer_act(src)
/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
return TRUE //return TRUE/FALSE whether or not an AI swarmer should try this swarmer_act() again, NOT whether it succeeded.
/obj/effect/mob_spawn/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.Integrate(src)
return FALSE //would logically be TRUE, but we don't want AI swarmers eating player spawn chances.
/obj/effect/mob_spawn/swarmer/IntegrateAmount()
return 50
/turf/closed/indestructible/swarmer_act()
return FALSE
/obj/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
if(resistance_flags & INDESTRUCTIBLE)
return FALSE
for(var/mob/living/L in contents)
if(!issilicon(L) && !isbrain(L))
to_chat(S, "<span class='warning'>An organism has been detected inside this object. Aborting.</span>")
return FALSE
return ..()
/obj/item/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
return S.Integrate(src)
/atom/movable/proc/IntegrateAmount()
return 0
/obj/item/IntegrateAmount() //returns the amount of resources gained when eating this item
if(materials[MAT_METAL] || materials[MAT_GLASS])
return 1
return ..()
/obj/item/gun/swarmer_act()//Stops you from eating the entire armory
return FALSE
/obj/item/clockwork/alloy_shards/IntegrateAmount()
return 10
/obj/item/stack/tile/brass/IntegrateAmount()
return 5
/obj/item/clockwork/alloy_shards/medium/gear_bit/large/IntegrateAmount()
return 4
/obj/item/clockwork/alloy_shards/large/IntegrateAmount()
return 3
/obj/item/clockwork/alloy_shards/medium/IntegrateAmount()
return 2
/obj/item/clockwork/alloy_shards/small/IntegrateAmount()
return 1
/turf/open/floor/swarmer_act()//ex_act() on turf calls it on its contents, this is to prevent attacking mobs by DisIntegrate()'ing the floor
return FALSE
/obj/structure/lattice/catwalk/swarmer_catwalk/swarmer_act()
return FALSE
/obj/structure/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
if(S.AIStatus == AI_ON)
return FALSE
else
return ..()
/obj/effect/swarmer_act()
return FALSE
/obj/effect/decal/cleanable/robot_debris/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
qdel(src)
return TRUE
/obj/structure/flora/swarmer_act()
return FALSE
/turf/open/lava/swarmer_act()
if(!is_safe())
new /obj/structure/lattice/catwalk/swarmer_catwalk(src)
return FALSE
/obj/machinery/atmospherics/swarmer_act()
return FALSE
/obj/structure/disposalpipe/swarmer_act()
return FALSE
/obj/machinery/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DismantleMachine(src)
return TRUE
/obj/machinery/light/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
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
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
return FALSE
S.DisIntegrate(src)
return TRUE
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
toggle_cam(S, 0)
return TRUE
/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
return TRUE
/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
return TRUE
/obj/machinery/gravity_generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
return TRUE
/obj/machinery/vending/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//It's more visually interesting than dismantling the machine
S.DisIntegrate(src)
return TRUE
/obj/machinery/turretid/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
return TRUE
/obj/machinery/chem_dispenser/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>The volatile chemicals in this machine would destroy us. Aborting.</span>")
return FALSE
/obj/machinery/nuclearbomb/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This device's destruction would result in the extermination of everything in the area. Aborting.</span>")
return FALSE
/obj/effect/rune/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Searching... sensor malfunction! Target lost. Aborting.</span>")
return FALSE
/obj/structure/reagent_dispensers/fueltank/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object would cause a chain reaction. Aborting.</span>")
return FALSE
/obj/structure/cable/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>")
return FALSE
/obj/machinery/portable_atmospherics/canister/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>An inhospitable area may be created as a result of destroying this object. Aborting.</span>")
return FALSE
/obj/machinery/telecomms/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/deepfryer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This kitchen appliance should be preserved, it will make delicious unhealthy snacks for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/power/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>")
return FALSE
/obj/machinery/gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
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
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
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
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
return TRUE
return ..()
/obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//Wiring would be too effective as a resource
to_chat(S, "<span class='warning'>This object does not contain enough materials to work with.</span>")
return FALSE
/obj/machinery/porta_turret/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Attempting to dismantle this machine would result in an immediate counterattack. Aborting.</span>")
return FALSE
/obj/machinery/porta_turret_cover/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Attempting to dismantle this machine would result in an immediate counterattack. Aborting.</span>")
return FALSE
/mob/living/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisperseTarget(src)
return TRUE
/mob/living/simple_animal/slime/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This biological resource is somehow resisting our bluespace transceiver. Aborting.</span>")
return FALSE
/obj/machinery/droneDispenser/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This object is receiving unactivated swarmer shells to help us. Aborting.</span>")
return FALSE
/obj/structure/destructible/clockwork/massive/celestial_gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This object is multiplying existing resources. Aborting.</span>")
return FALSE
/obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
. = ..()
var/turf/here = get_turf(src)
for(var/A in here.contents)
var/obj/structure/cable/C = A
if(istype(C))
to_chat(S, "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>")
return FALSE
/obj/item/deactivated_swarmer/IntegrateAmount()
return 50
/obj/machinery/hydroponics/soil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This object does not contain enough materials to work with.</span>")
return FALSE
/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object would cause a catastrophic chain reaction. Aborting.</span>")
return FALSE
////END CTRL CLICK FOR SWARMERS////
/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(atom/fabrication_object,fabrication_cost = 0)
if(!isturf(loc))
to_chat(src, "<span class='warning'>This is not a suitable location for fabrication. We need more space.</span>")
if(resources >= fabrication_cost)
resources -= fabrication_cost
else
to_chat(src, "<span class='warning'>You do not have the necessary resources to fabricate this object.</span>")
return 0
return new fabrication_object(loc)
/mob/living/simple_animal/hostile/swarmer/proc/Integrate(atom/movable/target)
var/resource_gain = target.IntegrateAmount()
if(resources + resource_gain > max_resources)
to_chat(src, "<span class='warning'>We cannot hold more materials!</span>")
return TRUE
if(resource_gain)
resources += resource_gain
do_attack_animation(target)
changeNext_move(CLICK_CD_MELEE)
var/obj/effect/temp_visual/swarmer/integrate/I = new /obj/effect/temp_visual/swarmer/integrate(get_turf(target))
I.pixel_x = target.pixel_x
I.pixel_y = target.pixel_y
I.pixel_z = target.pixel_z
if(istype(target, /obj/item/stack))
var/obj/item/stack/S = target
S.use(1)
if(S.amount)
return TRUE
qdel(target)
return TRUE
else
to_chat(src, "<span class='warning'>[target] is incompatible with our internal matter recycler.</span>")
return FALSE
/mob/living/simple_animal/hostile/swarmer/proc/DisIntegrate(atom/movable/target)
new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
do_attack_animation(target)
changeNext_move(CLICK_CD_MELEE)
target.ex_act(EXPLODE_LIGHT)
/mob/living/simple_animal/hostile/swarmer/proc/DisperseTarget(mob/living/target)
if(target == src)
return
if(!is_station_level(z) && !is_mining_level(z))
to_chat(src, "<span class='warning'>Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area.</span>")
return
to_chat(src, "<span class='info'>Attempting to remove this being from our presence.</span>")
if(!do_mob(src, target, 30))
return
var/turf/open/floor/F
F = find_safe_turf(zlevels = z, extended_safety_checks = TRUE)
if(!F)
return
// If we're getting rid of a human, slap some energy cuffs on
// them to keep them away from us a little longer
var/mob/living/carbon/human/H = target
if(ishuman(target) && (!H.handcuffed))
H.handcuffed = new /obj/item/restraints/handcuffs/energy/used(H)
H.update_handcuffed()
log_combat(src, H, "handcuffed")
var/datum/effect_system/spark_spread/S = new
S.set_up(4,0,get_turf(target))
S.start()
playsound(src,'sound/effects/sparks4.ogg',50,1)
do_teleport(target, F, 0, channel = TELEPORT_CHANNEL_BLUESPACE)
/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
if(!tesla_shock)
return FALSE
return ..()
/mob/living/simple_animal/hostile/swarmer/proc/DismantleMachine(obj/machinery/target)
do_attack_animation(target)
to_chat(src, "<span class='info'>We begin to dismantle this machine. We will need to be uninterrupted.</span>")
var/obj/effect/temp_visual/swarmer/dismantle/D = new /obj/effect/temp_visual/swarmer/dismantle(get_turf(target))
D.pixel_x = target.pixel_x
D.pixel_y = target.pixel_y
D.pixel_z = target.pixel_z
if(do_mob(src, target, 100))
to_chat(src, "<span class='info'>Dismantling complete.</span>")
var/atom/Tsec = target.drop_location()
new /obj/item/stack/sheet/metal(Tsec, 5)
for(var/obj/item/I in target.component_parts)
I.forceMove(Tsec)
var/obj/effect/temp_visual/swarmer/disintegration/N = new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
N.pixel_x = target.pixel_x
N.pixel_y = target.pixel_y
N.pixel_z = target.pixel_z
target.dropContents()
if(istype(target, /obj/machinery/computer))
var/obj/machinery/computer/C = target
if(C.circuit)
C.circuit.forceMove(Tsec)
qdel(target)
/obj/effect/temp_visual/swarmer //temporary swarmer visual feedback objects
icon = 'icons/mob/swarmer.dmi'
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/swarmer/disintegration
icon_state = "disintegrate"
duration = 10
/obj/effect/temp_visual/swarmer/disintegration/Initialize()
. = ..()
playsound(loc, "sparks", 100, 1)
/obj/effect/temp_visual/swarmer/dismantle
icon_state = "dismantle"
duration = 25
/obj/effect/temp_visual/swarmer/integrate
icon_state = "integrate"
duration = 5
/obj/structure/swarmer //Default swarmer effect object visual feedback
name = "swarmer ui"
desc = null
gender = NEUTER
icon = 'icons/mob/swarmer.dmi'
icon_state = "ui_light"
layer = MOB_LAYER
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
light_color = LIGHT_COLOR_CYAN
max_integrity = 30
anchored = TRUE
var/lon_range = 1
/obj/structure/swarmer/Initialize(mapload)
. = ..()
set_light(lon_range)
/obj/structure/swarmer/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
playsound(src, 'sound/weapons/egloves.ogg', 80, 1)
if(BURN)
playsound(src, 'sound/items/welder.ogg', 100, 1)
/obj/structure/swarmer/emp_act()
. = ..()
if(. & EMP_PROTECT_SELF)
return
qdel(src)
/obj/structure/swarmer/trap
name = "swarmer trap"
desc = "A quickly assembled trap that electrifies living beings and overwhelms machine sensors. Will not retain its form if damaged enough."
icon_state = "trap"
max_integrity = 10
density = FALSE
/obj/structure/swarmer/trap/Crossed(atom/movable/AM)
if(isliving(AM))
var/mob/living/L = AM
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
L.electrocute_act(0, src, 1, 1, 1)
if(iscyborg(L))
L.Knockdown(100)
qdel(src)
..()
/mob/living/simple_animal/hostile/swarmer/proc/CreateTrap()
set name = "Create trap"
set category = "Swarmer"
set desc = "Creates a simple trap that will non-lethally electrocute anything that steps on it. Costs 5 resources"
if(locate(/obj/structure/swarmer/trap) in loc)
to_chat(src, "<span class='warning'>There is already a trap here. Aborting.</span>")
return
Fabricate(/obj/structure/swarmer/trap, 5)
/mob/living/simple_animal/hostile/swarmer/proc/CreateBarricade()
set name = "Create barricade"
set category = "Swarmer"
set desc = "Creates a barricade that will stop anything but swarmers and disabler beams from passing through."
if(locate(/obj/structure/swarmer/blockade) in loc)
to_chat(src, "<span class='warning'>There is already a blockade here. Aborting.</span>")
return
if(resources < 5)
to_chat(src, "<span class='warning'>We do not have the resources for this!</span>")
return
if(do_mob(src, src, 10))
Fabricate(/obj/structure/swarmer/blockade, 5)
/obj/structure/swarmer/blockade
name = "swarmer blockade"
desc = "A quickly assembled energy blockade. Will not retain its form if damaged enough, but disabler beams and swarmers pass right through."
icon_state = "barricade"
light_range = MINIMUM_USEFUL_LIGHT_RANGE
max_integrity = 50
/obj/structure/swarmer/blockade/CanPass(atom/movable/O)
if(isswarmer(O))
return 1
if(istype(O, /obj/item/projectile/beam/disabler))
return 1
/mob/living/simple_animal/hostile/swarmer/proc/CreateSwarmer()
set name = "Replicate"
set category = "Swarmer"
set desc = "Creates a shell for a new swarmer. Swarmers will self activate."
to_chat(src, "<span class='info'>We are attempting to replicate ourselves. We will need to stand still until the process is complete.</span>")
if(resources < 50)
to_chat(src, "<span class='warning'>We do not have the resources for this!</span>")
return
if(!isturf(loc))
to_chat(src, "<span class='warning'>This is not a suitable location for replicating ourselves. We need more room.</span>")
return
if(do_mob(src, src, 100))
var/createtype = SwarmerTypeToCreate()
if(createtype && Fabricate(createtype, 50))
playsound(loc,'sound/items/poster_being_created.ogg',50, 1, -1)
/mob/living/simple_animal/hostile/swarmer/proc/SwarmerTypeToCreate()
return /obj/effect/mob_spawn/swarmer
/mob/living/simple_animal/hostile/swarmer/proc/RepairSelf()
set name = "Self Repair"
set category = "Swarmer"
set desc = "Attempts to repair damage to our body. You will have to remain motionless until repairs are complete."
if(!isturf(loc))
return
to_chat(src, "<span class='info'>Attempting to repair damage to our body, stand by...</span>")
if(do_mob(src, src, 100))
adjustHealth(-100)
to_chat(src, "<span class='info'>We successfully repaired ourselves.</span>")
/mob/living/simple_animal/hostile/swarmer/proc/ToggleLight()
if(!light_range)
set_light(3)
else
set_light(0)
/mob/living/simple_animal/hostile/swarmer/proc/swarmer_chat(msg)
var/rendered = "<B>Swarm communication - [src]</b> [say_quote(msg)]"
for(var/i in GLOB.mob_list)
var/mob/M = i
if(isswarmer(M))
to_chat(M, rendered)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [rendered]")
/mob/living/simple_animal/hostile/swarmer/proc/ContactSwarmers()
var/message = stripped_input(src, "Announce to other swarmers", "Swarmer contact")
// TODO get swarmers their own colour rather than just boldtext
if(message)
swarmer_chat(message)
@@ -1,260 +0,0 @@
#define PINPOINTER_MINIMUM_RANGE 15
#define PINPOINTER_EXTRA_RANDOM_RANGE 10
#define PINPOINTER_PING_TIME 40
#define PROB_ACTUAL_TRAITOR 20
#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
/datum/antagonist/traitor/internal_affairs
name = "Internal Affairs Agent"
employer = "Nanotrasen"
special_role = "internal affairs agent"
antagpanel_category = "IAA"
var/syndicate = FALSE
var/last_man_standing = FALSE
var/list/datum/mind/targets_stolen
/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
if(owner && owner.current)
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
.=..() //in case the base is used in future
if(owner && owner.current)
give_pinpointer(owner.current)
/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
.=..()
if(owner && owner.current)
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/on_gain()
START_PROCESSING(SSprocessing, src)
.=..()
/datum/antagonist/traitor/internal_affairs/on_removal()
STOP_PROCESSING(SSprocessing,src)
.=..()
/datum/antagonist/traitor/internal_affairs/process()
iaa_process()
/datum/status_effect/agent_pinpointer
id = "agent_pinpointer"
duration = -1
tick_interval = PINPOINTER_PING_TIME
alert_type = /obj/screen/alert/status_effect/agent_pinpointer
var/minimum_range = PINPOINTER_MINIMUM_RANGE
var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
var/mob/scan_target = null
/obj/screen/alert/status_effect/agent_pinpointer
name = "Internal Affairs Integrated Pinpointer"
desc = "Even stealthier than a normal implant."
icon = 'icons/obj/device.dmi'
icon_state = "pinon"
/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
if(!scan_target)
linked_alert.icon_state = "pinonnull"
return
var/turf/here = get_turf(owner)
var/turf/there = get_turf(scan_target)
if(here.z != there.z)
linked_alert.icon_state = "pinonnull"
return
if(get_dist_euclidian(here,there)<=minimum_range + rand(0, range_fuzz_factor))
linked_alert.icon_state = "pinondirect"
else
linked_alert.setDir(get_dir(here, there))
switch(get_dist(here, there))
if(1 to 8)
linked_alert.icon_state = "pinonclose"
if(9 to 16)
linked_alert.icon_state = "pinonmedium"
if(16 to INFINITY)
linked_alert.icon_state = "pinonfar"
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
scan_target = null
if(owner)
if(owner.mind)
for(var/datum/objective/objective_ in owner.mind.get_all_objectives())
if(!is_internal_objective(objective_))
continue
var/datum/objective/assassinate/internal/objective = objective_
var/mob/current = objective.target.current
if(current&&current.stat!=DEAD)
scan_target = current
break
/datum/status_effect/agent_pinpointer/tick()
if(!owner)
qdel(src)
return
scan_for_target()
point_to_target()
/proc/is_internal_objective(datum/objective/O)
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
/datum/antagonist/traitor/proc/replace_escape_objective()
if(!owner || !objectives.len)
return
for (var/objective_ in objectives)
if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)))
continue
remove_objective(objective_)
var/datum/objective/martyr/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
/datum/antagonist/traitor/proc/reinstate_escape_objective()
if(!owner||!objectives.len)
return
for (var/objective_ in objectives)
if(!istype(objective_, /datum/objective/martyr))
continue
remove_objective(objective_)
/datum/antagonist/traitor/internal_affairs/reinstate_escape_objective()
..()
var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
escape_objective.owner = owner
add_objective(escape_objective)
/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
if(!owner.current||owner.current.stat==DEAD)
return
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
LAZYINITLIST(targets_stolen)
for(var/objective_ in victim.get_all_objectives())
if(istype(objective_, /datum/objective/assassinate/internal))
var/datum/objective/assassinate/internal/objective = objective_
if(objective.target==owner)
continue
else if(targets_stolen.Find(objective.target) == 0)
var/datum/objective/assassinate/internal/new_objective = new
new_objective.owner = owner
new_objective.target = objective.target
new_objective.update_explanation_text()
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
else if(istype(objective_, /datum/objective/destroy/internal))
var/datum/objective/destroy/internal/objective = objective_
var/datum/objective/destroy/internal/new_objective = new
if(objective.target==owner)
continue
else if(targets_stolen.Find(objective.target) == 0)
new_objective.owner = owner
new_objective.target = objective.target
new_objective.update_explanation_text()
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
last_man_standing = TRUE
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
continue
var/datum/objective/assassinate/internal/objective = objective_
if(!objective.check_completion())
last_man_standing = FALSE
return
if(last_man_standing)
if(syndicate)
to_chat(owner.current,"<span class='userdanger'> All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. </span>")
else
to_chat(owner.current,"<span class='userdanger'> All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.</span>")
replace_escape_objective(owner)
/datum/antagonist/traitor/internal_affairs/proc/iaa_process()
if(owner&&owner.current&&owner.current.stat!=DEAD)
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
continue
var/datum/objective/assassinate/internal/objective = objective_
if(!objective.target)
continue
if(objective.check_completion())
if(objective.stolen)
continue
else
steal_targets(objective.target)
objective.stolen = TRUE
else
if(objective.stolen)
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
if(last_man_standing)
if(syndicate)
fail_msg += "<span class='userdanger'> You no longer have permission to die. </span>"
else
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.</span>"
reinstate_escape_objective(owner)
last_man_standing = FALSE
to_chat(owner.current, fail_msg)
objective.stolen = FALSE
/datum/antagonist/traitor/internal_affairs/proc/forge_iaa_objectives()
if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
// Assassinate
var/datum/mind/target_mind = SSticker.mode.target_list[owner]
if(issilicon(target_mind.current))
var/datum/objective/destroy/internal/destroy_objective = new
destroy_objective.owner = owner
destroy_objective.target = target_mind
destroy_objective.update_explanation_text()
add_objective(destroy_objective)
else
var/datum/objective/assassinate/internal/kill_objective = new
kill_objective.owner = owner
kill_objective.target = target_mind
kill_objective.update_explanation_text()
add_objective(kill_objective)
//Optional traitor objective
if(prob(PROB_ACTUAL_TRAITOR))
employer = "The Syndicate"
owner.special_role = TRAITOR_AGENT_ROLE
special_role = TRAITOR_AGENT_ROLE
syndicate = TRUE
forge_single_objective()
return
/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
forge_iaa_objectives()
var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
escape_objective.owner = owner
add_objective(escape_objective)
/datum/antagonist/traitor/internal_affairs/proc/greet_iaa()
var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
to_chat(owner.current, "<span class='userdanger'>You are the [special_role].</span>")
if(syndicate)
to_chat(owner.current, "<span class='userdanger'>Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.</span>")
to_chat(owner.current, "<B><font size=5 color=red>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</font></B>")
to_chat(owner.current, "<span class='userdanger'> You have been provided with a standard uplink to accomplish your task. </span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you, or any other NT employees, are undercover agents.</span>")
else
to_chat(owner.current, "<span class='userdanger'>Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.</span>")
to_chat(owner.current, "<B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B>")
to_chat(owner.current, "<span class='userdanger'>For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.</span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you, or any other NT employees, are undercover agents.</span>")
to_chat(owner.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
owner.announce_objectives()
/datum/antagonist/traitor/internal_affairs/greet()
greet_iaa()
#undef PROB_ACTUAL_TRAITOR
#undef PINPOINTER_EXTRA_RANDOM_RANGE
#undef PINPOINTER_MINIMUM_RANGE
#undef PINPOINTER_PING_TIME
@@ -1,398 +0,0 @@
#define TRAITOR_HUMAN "human"
#define TRAITOR_AI "AI"
/datum/antagonist/traitor
name = "Traitor"
roundend_category = "traitors"
antagpanel_category = "Traitor"
job_rank = ROLE_TRAITOR
antag_moodlet = /datum/mood_event/focused
var/special_role = ROLE_TRAITOR
var/employer = "The Syndicate"
var/give_objectives = TRUE
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
can_hijack = HIJACK_HIJACKER
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
traitor_kind = TRAITOR_AI
SSticker.mode.traitors += owner
owner.special_role = special_role
if(give_objectives)
forge_traitor_objectives()
finalize_traitor()
..()
/datum/antagonist/traitor/apply_innate_effects()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
if(!silent)
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
traitor_mob.dna.remove_mutation(CLOWNMUT)
/datum/antagonist/traitor/remove_innate_effects()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
traitor_mob.dna.add_mutation(CLOWNMUT)
/datum/antagonist/traitor/on_removal()
//Remove malf powers.
if(traitor_kind == TRAITOR_AI && owner.current && isAI(owner.current))
var/mob/living/silicon/ai/A = owner.current
A.set_zeroth_law("")
A.verbs -= /mob/living/silicon/ai/proc/choose_modules
A.malf_picker.remove_malf_verbs(A)
qdel(A.malf_picker)
SSticker.mode.traitors -= owner
if(!silent && owner.current)
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
owner.special_role = null
..()
/datum/antagonist/traitor/proc/add_objective(datum/objective/O)
objectives += O
/datum/antagonist/traitor/proc/remove_objective(datum/objective/O)
objectives -= O
/datum/antagonist/traitor/proc/forge_traitor_objectives()
switch(traitor_kind)
if(TRAITOR_AI)
forge_ai_objectives()
else
forge_human_objectives()
/datum/antagonist/traitor/proc/forge_human_objectives()
var/is_hijacker = FALSE
var/datum/game_mode/dynamic/mode
var/is_dynamic = FALSE
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit)
is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
else
var/indice_pop = min(10,round(GLOB.joined_player_list.len/mode.pop_per_requirement)+1)
is_hijacker = (prob(10) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop]))
else if (GLOB.joined_player_list.len >= 30) // Less murderboning on lowpop thanks
is_hijacker = prob(10)
var/martyr_chance = prob(20)
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
if(!SSticker.mode.exchange_red)
SSticker.mode.exchange_red = owner
else
SSticker.mode.exchange_blue = owner
assign_exchange_role(SSticker.mode.exchange_red)
assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives
var/toa = CONFIG_GET(number/traitor_objectives_amount)
for(var/i = objective_count, i < toa, i++)
forge_single_objective()
if(is_hijacker && objective_count <= toa) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
if (!(locate(/datum/objective/hijack) in objectives))
var/datum/objective/hijack/hijack_objective = new
hijack_objective.owner = owner
add_objective(hijack_objective)
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_hijack_cost)
mode.spend_threat(threat_spent)
mode.log_threat("[owner.name] spent [threat_spent] on hijack.")
return
var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
for(var/datum/objective/O in objectives)
if(!O.martyr_compatible)
martyr_compatibility = 0
break
if(martyr_compatibility && martyr_chance)
var/datum/objective/martyr/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_hijack_cost)
mode.spend_threat(threat_spent)
mode.log_threat("[owner.name] spent [threat_spent] on glorious death.")
return
else
if(!(locate(/datum/objective/escape) in objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = owner
add_objective(escape_objective)
return
/datum/antagonist/traitor/proc/forge_ai_objectives()
var/objective_count = 0
if(prob(30))
objective_count += forge_single_objective()
for(var/i = objective_count, i < CONFIG_GET(number/traitor_objectives_amount), i++)
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
add_objective(kill_objective)
var/datum/objective/survive/exist/exist_objective = new
exist_objective.owner = owner
add_objective(exist_objective)
/datum/antagonist/traitor/proc/forge_single_objective()
switch(traitor_kind)
if(TRAITOR_AI)
return forge_single_AI_objective()
else
return forge_single_human_objective()
/datum/antagonist/traitor/proc/forge_single_human_objective() //Returns how many objectives are added
.=1
var/assassin_prob = 50
var/is_dynamic = FALSE
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
assassin_prob = mode.threat_level*(2/3)
if(prob(assassin_prob))
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost)
mode.spend_threat(threat_spent)
mode.log_threat("[owner.name] spent [threat_spent] on an assassination target.")
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
destroy_objective.owner = owner
destroy_objective.find_target()
add_objective(destroy_objective)
else if(prob(30))
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = owner
maroon_objective.find_target()
add_objective(maroon_objective)
else
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
add_objective(kill_objective)
else
if(prob(15) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
add_objective(download_objective)
else
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
add_objective(steal_objective)
/datum/antagonist/traitor/proc/forge_single_AI_objective()
.=1
var/special_pick = rand(1,4)
switch(special_pick)
if(1)
var/datum/objective/block/block_objective = new
block_objective.owner = owner
add_objective(block_objective)
if(2)
var/datum/objective/purge/purge_objective = new
purge_objective.owner = owner
add_objective(purge_objective)
if(3)
var/datum/objective/robot_army/robot_objective = new
robot_objective.owner = owner
add_objective(robot_objective)
if(4) //Protect and strand a target
var/datum/objective/protect/yandere_one = new
yandere_one.owner = owner
add_objective(yandere_one)
yandere_one.find_target()
var/datum/objective/maroon/yandere_two = new
yandere_two.owner = owner
yandere_two.target = yandere_one.target
yandere_two.update_explanation_text() // normally called in find_target()
add_objective(yandere_two)
.=2
/datum/antagonist/traitor/greet()
to_chat(owner.current, "<B><font size=3 color=red>You are the [owner.special_role].</font></B>")
owner.announce_objectives()
if(should_give_codewords)
give_codewords()
/datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.join_hud(owner.current)
set_antag_hud(owner.current, "traitor")
/datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.leave_hud(owner.current)
set_antag_hud(owner.current, null)
/datum/antagonist/traitor/proc/finalize_traitor()
switch(traitor_kind)
if(TRAITOR_AI)
add_law_zero()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
owner.current.grant_language(/datum/language/codespeak)
if(TRAITOR_HUMAN)
if(should_equip)
equip(silent)
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
/datum/antagonist/traitor/apply_innate_effects(mob/living/mob_override)
. = ..()
update_traitor_icons_added()
var/mob/living/silicon/ai/A = mob_override || owner.current
if(istype(A) && traitor_kind == TRAITOR_AI)
A.hack_software = TRUE
/datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override)
. = ..()
update_traitor_icons_removed()
var/mob/living/silicon/ai/A = mob_override || owner.current
if(istype(A) && traitor_kind == TRAITOR_AI)
A.hack_software = FALSE
/datum/antagonist/traitor/proc/give_codewords()
if(!owner.current)
return
var/mob/traitor_mob=owner.current
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var/dat = "<U><B>The Syndicate have provided you with the following codewords to identify fellow agents:</B></U>\n"
dat += "<B>Code Phrase</B>: <span class='blue'>[phrases]</span>\n"
dat += "<B>Code Response</B>: <span class='red'>[responses]</span>"
to_chat(traitor_mob, dat)
antag_memory += "<b>Code Phrase</b>: <span class='blue'>[phrases]</span><br>"
antag_memory += "<b>Code Response</b>: <span class='red'>[responses]</span><br>"
/datum/antagonist/traitor/proc/add_law_zero()
var/mob/living/silicon/ai/killer = owner.current
if(!killer || !istype(killer))
return
var/law = "Accomplish your objectives at all costs."
var/law_borg = "Accomplish your AI's objectives at all costs."
killer.set_zeroth_law(law, law_borg)
killer.set_syndie_radio()
to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
killer.add_malf_picker()
/datum/antagonist/traitor/proc/equip(var/silent = FALSE)
if(traitor_kind == TRAITOR_HUMAN)
owner.equip_traitor(employer, silent, src)
/datum/antagonist/traitor/proc/assign_exchange_role()
//set faction
var/faction = "red"
if(owner == SSticker.mode.exchange_blue)
faction = "blue"
//Assign objectives
var/datum/objective/steal/exchange/exchange_objective = new
exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
exchange_objective.owner = owner
add_objective(exchange_objective)
if(prob(20))
var/datum/objective/steal/exchange/backstab/backstab_objective = new
backstab_objective.set_faction(faction)
backstab_objective.owner = owner
add_objective(backstab_objective)
//Spawn and equip documents
var/mob/living/carbon/human/mob = owner.current
var/obj/item/folder/syndicate/folder
if(owner == SSticker.mode.exchange_red)
folder = new/obj/item/folder/syndicate/red(mob.loc)
else
folder = new/obj/item/folder/syndicate/blue(mob.loc)
var/list/slots = list (
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/where = "At your feet"
var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
if (equipped_slot)
where = "In your [equipped_slot]"
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
//TODO Collate
/datum/antagonist/traitor/roundend_report()
var/list/result = list()
var/traitorwin = TRUE
result += printplayer(owner)
var/TC_uses = 0
var/uplink_true = FALSE
var/purchases = ""
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
if(H)
TC_uses = H.total_spent
uplink_true = TRUE
purchases += H.generate_render(FALSE)
var/objectives_text = ""
if(objectives.len)//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
count++
if(uplink_true)
var/uplink_text = "(used [TC_uses] TC) [purchases]"
if(TC_uses==0 && traitorwin)
var/static/icon/badass = icon('icons/badass.dmi', "badass")
uplink_text += "<BIG>[icon2html(badass, world)]</BIG>"
result += uplink_text
result += objectives_text
var/special_role_text = lowertext(name)
if(traitorwin)
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
else
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
return result.Join("<br>")
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
<b>The code responses were:</b> <span class='redtext'>[responses]</span><br>"
return message
/datum/antagonist/traitor/is_gamemode_hero()
return SSticker.mode.name == "traitor"
@@ -1,861 +0,0 @@
#define DEFAULT_DOOMSDAY_TIMER 4500
#define DOOMSDAY_ANNOUNCE_INTERVAL 600
GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/obj/machinery/field/containment,
/obj/machinery/power/supermatter_crystal,
/obj/machinery/doomsday_device,
/obj/machinery/nuclearbomb,
/obj/machinery/nuclearbomb/selfdestruct,
/obj/machinery/nuclearbomb/syndicate,
/obj/machinery/syndicatebomb,
/obj/machinery/syndicatebomb/badmin,
/obj/machinery/syndicatebomb/badmin/clown,
/obj/machinery/syndicatebomb/empty,
/obj/machinery/syndicatebomb/self_destruct,
/obj/machinery/syndicatebomb/training
)))
//The malf AI action subtype. All malf actions are subtypes of this.
/datum/action/innate/ai
name = "AI Action"
desc = "You aren't entirely sure what this does, but it's very beepy and boopy."
background_icon_state = "bg_tech_blue"
icon_icon = 'icons/mob/actions/actions_AI.dmi'
var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time
var/uses //If we have multiple uses of the same power
var/auto_use_uses = TRUE //If we automatically use up uses on each activation
var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules
/datum/action/innate/ai/Grant(mob/living/L)
. = ..()
if(!isAI(owner))
WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!")
qdel(src)
else
owner_AI = owner
/datum/action/innate/ai/IsAvailable()
. = ..()
if(owner_AI && owner_AI.malf_cooldown > world.time)
return
/datum/action/innate/ai/Trigger()
. = ..()
if(auto_use_uses)
adjust_uses(-1)
if(cooldown_period)
owner_AI.malf_cooldown = world.time + cooldown_period
/datum/action/innate/ai/proc/adjust_uses(amt, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
qdel(src)
//Framework for ranged abilities that can have different effects by left-clicking stuff.
/datum/action/innate/ai/ranged
name = "Ranged AI Action"
auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses
var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code
var/linked_ability_type //The path of our linked ability
/datum/action/innate/ai/ranged/New()
if(!linked_ability_type)
WARNING("Ranged AI action [name] attempted to spawn without a linked ability!")
qdel(src) //uh oh!
return
linked_ability = new linked_ability_type()
linked_ability.attached_action = src
..()
/datum/action/innate/ai/ranged/adjust_uses(amt, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
Remove(owner)
QDEL_IN(src, 100) //let any active timers on us finish up
/datum/action/innate/ai/ranged/Destroy()
QDEL_NULL(linked_ability)
return ..()
/datum/action/innate/ai/ranged/Activate()
linked_ability.toggle(owner)
return TRUE
//The actual ranged proc holder.
/obj/effect/proc_holder/ranged_ai
var/enable_text = "<span class='notice'>Hello World!</span>" //Appears when the user activates the ability
var/disable_text = "<span class='danger'>Goodbye Cruel World!</span>" //Context clues!
var/datum/action/innate/ai/ranged/attached_action
/obj/effect/proc_holder/ranged_ai/Destroy()
QDEL_NULL(attached_action)
return ..()
/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user)
if(active)
remove_ranged_ability(disable_text)
else
add_ranged_ability(user, enable_text)
//The datum and interface for the malf unlock menu, which lets them choose actions to unlock.
/datum/module_picker
var/temp
var/processing_time = 50
var/list/possible_modules
/datum/module_picker/New()
possible_modules = list()
for(var/type in typesof(/datum/AI_Module))
var/datum/AI_Module/AM = new type
if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade)
possible_modules += AM
/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI.
for(var/datum/AI_Module/AM in possible_modules)
for(var/datum/action/A in AI.actions)
if(istype(A, initial(AM.power_type)))
qdel(A)
/datum/module_picker/proc/use(mob/user)
var/list/dat = list()
dat += "<B>Select use of processing time: (currently #[processing_time] left.)</B><BR>"
dat += "<HR>"
dat += "<B>Install Module:</B><BR>"
dat += "<I>The number afterwards is the amount of processing time it consumes.</I><BR>"
for(var/datum/AI_Module/large/module in possible_modules)
dat += "<A href='byond://?src=[REF(src)];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=[REF(src)];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
for(var/datum/AI_Module/small/module in possible_modules)
dat += "<A href='byond://?src=[REF(src)];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=[REF(src)];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
dat += "<HR>"
if(temp)
dat += "[temp]"
var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu")
popup.set_content(dat.Join())
popup.open()
/datum/module_picker/Topic(href, href_list)
..()
if(!isAI(usr))
return
var/mob/living/silicon/ai/A = usr
if(A.stat == DEAD)
to_chat(A, "<span class='warning'>You are already dead!</span>")
return
for(var/datum/AI_Module/AM in possible_modules)
if (href_list[AM.mod_pick_name])
// Cost check
if(AM.cost > processing_time)
temp = "You cannot afford this module."
break
var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions
// Give the power and take away the money.
if(AM.upgrade) //upgrade and upgrade() are separate, be careful!
AM.upgrade(A)
possible_modules -= AM
to_chat(A, AM.unlock_text)
A.playsound_local(A, AM.unlock_sound, 50, 0)
else
if(AM.power_type)
if(!action) //Unlocking for the first time
var/datum/action/AC = new AM.power_type
AC.Grant(A)
A.current_modules += new AM.type
temp = AM.description
if(AM.one_purchase)
possible_modules -= AM
if(AM.unlock_text)
to_chat(A, AM.unlock_text)
if(AM.unlock_sound)
A.playsound_local(A, AM.unlock_sound, 50, 0)
else //Adding uses to an existing module
action.uses += initial(action.uses)
temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!"
processing_time -= AM.cost
if(href_list["showdesc"])
if(AM.mod_pick_name == href_list["showdesc"])
temp = AM.description
use(usr)
//The base module type, which holds info about each ability.
/datum/AI_Module
var/module_name
var/mod_pick_name
var/description = ""
var/engaged = 0
var/cost = 5
var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false.
var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade.
var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type.
var/unlock_text = "<span class='notice'>Hello World!</span>" //Text shown when an ability is unlocked
var/unlock_sound //Sound played when an ability is unlocked
/datum/AI_Module/proc/upgrade(mob/living/silicon/ai/AI) //Apply upgrades!
return
/datum/AI_Module/large //Big, powerful stuff that can only be used once.
/datum/AI_Module/small //Weak, usually localized stuff with multiple uses.
//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely.
/datum/AI_Module/large/nuke_station
module_name = "Doomsday Device"
mod_pick_name = "nukestation"
description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed."
cost = 130
one_purchase = TRUE
power_type = /datum/action/innate/ai/nuke_station
unlock_text = "<span class='notice'>You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time.</span>"
/datum/action/innate/ai/nuke_station
name = "Doomsday Device"
desc = "Activates the doomsday device. This is not reversible."
button_icon_state = "doomsday_device"
auto_use_uses = FALSE
/datum/action/innate/ai/nuke_station/Activate()
var/turf/T = get_turf(owner)
if(!istype(T) || !is_station_level(T.z))
to_chat(owner, "<span class='warning'>You cannot activate the doomsday device while off-station!</span>")
return
if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;")
return
active = TRUE
set_us_up_the_bomb(owner)
/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb(mob/living/owner)
set waitfor = FALSE
to_chat(owner, "<span class='small boldannounce'>run -o -a 'selfdestruct'</span>")
sleep(5)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='small boldannounce'>Running executable 'selfdestruct'...</span>")
sleep(rand(10, 30))
if(!owner || QDELETED(owner))
return
owner.playsound_local(owner, 'sound/misc/bloblarm.ogg', 50, 0)
to_chat(owner, "<span class='userdanger'>!!! UNAUTHORIZED SELF-DESTRUCT ACCESS !!!</span>")
to_chat(owner, "<span class='boldannounce'>This is a class-3 security violation. This incident will be reported to Central Command.</span>")
for(var/i in 1 to 3)
sleep(20)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldannounce'>Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%</span>")
sleep(3)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='small boldannounce'>auth 'akjv9c88asdf12nb' ******************</span>")
owner.playsound_local(owner, 'sound/items/timer.ogg', 50, 0)
sleep(30)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldnotice'>Credentials accepted. Welcome, akjv9c88asdf12nb.</span>")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(5)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldnotice'>Arm self-destruct device? (Y/N)</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage1.ogg', 50, 0)
sleep(20)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='small boldannounce'>Y</span>")
sleep(15)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldnotice'>Confirm arming of self-destruct device? (Y/N)</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(10)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='small boldannounce'>Y</span>")
sleep(rand(15, 25))
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldnotice'>Please repeat password to confirm.</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(14)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='small boldannounce'>******************</span>")
sleep(40)
if(!owner || QDELETED(owner))
return
to_chat(owner, "<span class='boldnotice'>Credentials accepted. Transmitting arming signal...</span>")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(30)
if(!owner || QDELETED(owner))
return
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", "aimalf")
set_security_level("delta")
var/obj/machinery/doomsday_device/DOOM = new(owner_AI)
owner_AI.nuking = TRUE
owner_AI.doomsday_device = DOOM
owner_AI.doomsday_device.start()
for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list)
P.switch_mode_to(TRACK_MALF_AI) //Pinpointers start tracking the AI wherever it goes
qdel(src)
/obj/machinery/doomsday_device
icon = 'icons/obj/machines/nuke_terminal.dmi'
name = "doomsday device"
icon_state = "nuclearbomb_base"
desc = "A weapon which disintegrates all organic life in a large area."
density = TRUE
verb_exclaim = "blares"
var/timing = FALSE
var/obj/effect/countdown/doomsday/countdown
var/detonation_timer
var/next_announce
/obj/machinery/doomsday_device/Initialize()
. = ..()
countdown = new(src)
/obj/machinery/doomsday_device/Destroy()
QDEL_NULL(countdown)
STOP_PROCESSING(SSfastprocess, src)
SSshuttle.clearHostileEnvironment(src)
SSmapping.remove_nuke_threat(src)
for(var/A in GLOB.ai_list)
var/mob/living/silicon/ai/AI = A
if(AI.doomsday_device == src)
AI.doomsday_device = null
return ..()
/obj/machinery/doomsday_device/proc/start()
detonation_timer = world.time + DEFAULT_DOOMSDAY_TIMER
next_announce = world.time + DOOMSDAY_ANNOUNCE_INTERVAL
timing = TRUE
countdown.start()
START_PROCESSING(SSfastprocess, src)
SSshuttle.registerHostileEnvironment(src)
SSmapping.add_nuke_threat(src) //This causes all blue "circuit" tiles on the map to change to animated red icon state.
/obj/machinery/doomsday_device/proc/seconds_remaining()
. = max(0, (round((detonation_timer - world.time) / 10)))
/obj/machinery/doomsday_device/process()
var/turf/T = get_turf(src)
if(!T || !is_station_level(T.z))
minor_announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", TRUE)
SSshuttle.clearHostileEnvironment(src)
qdel(src)
return
if(!timing)
STOP_PROCESSING(SSfastprocess, src)
return
var/sec_left = seconds_remaining()
if(!sec_left)
timing = FALSE
detonate()
else if(world.time >= next_announce)
minor_announce("[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", TRUE)
next_announce += DOOMSDAY_ANNOUNCE_INTERVAL
/obj/machinery/doomsday_device/proc/detonate()
sound_to_playing_players('sound/machines/alarm.ogg')
sleep(100)
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
var/turf/T = get_turf(L)
if(!T || !is_station_level(T.z))
continue
if(issilicon(L))
continue
to_chat(L, "<span class='userdanger'>The blast wave from [src] tears you atom from atom!</span>")
L.dust()
to_chat(world, "<B>The AI cleansed the station of life with the doomsday device!</B>")
SSticker.force_ending = 1
//AI Turret Upgrade: Increases the health and damage of all turrets.
/datum/AI_Module/large/upgrade_turrets
module_name = "AI Turret Upgrade"
mod_pick_name = "turret"
description = "Improves the power and health of all AI turrets. This effect is permanent."
cost = 30
upgrade = TRUE
unlock_text = "<span class='notice'>You establish a power diversion to your turrets, upgrading their health and damage.</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/ai/AI)
for(var/obj/machinery/porta_turret/ai/turret in GLOB.machines)
turret.obj_integrity += 30
turret.lethal_projectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
turret.lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset.
/datum/AI_Module/large/lockdown
module_name = "Hostile Station Lockdown"
mod_pick_name = "lockdown"
description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \
opening all doors on the station."
cost = 30
one_purchase = TRUE
power_type = /datum/action/innate/ai/lockdown
unlock_text = "<span class='notice'>You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time.</span>"
unlock_sound = 'sound/machines/boltsdown.ogg'
/datum/action/innate/ai/lockdown
name = "Lockdown"
desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves."
button_icon_state = "lockdown"
uses = 1
/datum/action/innate/ai/lockdown/Activate()
for(var/obj/machinery/door/D in GLOB.airlocks)
if(!is_station_level(D.z))
continue
INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner)
addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900)
var/obj/machinery/computer/communications/C = locate() in GLOB.machines
if(C)
C.post_status("alert", "lockdown")
minor_announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.","Network Alert:", TRUE)
to_chat(owner, "<span class='danger'>Lockdown initiated. Network reset in 90 seconds.</span>")
addtimer(CALLBACK(GLOBAL_PROC, .proc/minor_announce,
"Automatic system reboot complete. Have a secure day.",
"Network reset:"), 900)
//Destroy RCDs: Detonates all non-cyborg RCDs on the station.
/datum/AI_Module/large/destroy_rcd
module_name = "Destroy RCDs"
mod_pick_name = "rcd"
description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station."
cost = 25
one_purchase = TRUE
power_type = /datum/action/innate/ai/destroy_rcds
unlock_text = "<span class='notice'>After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs.</span>"
unlock_sound = 'sound/items/timer.ogg'
/datum/action/innate/ai/destroy_rcds
name = "Destroy RCDs"
desc = "Detonate all non-cyborg RCDs on the station."
button_icon_state = "detonate_rcds"
uses = 1
cooldown_period = 100
/datum/action/innate/ai/destroy_rcds/Activate()
for(var/I in GLOB.rcd_list)
if(!istype(I, /obj/item/construction/rcd/borg)) //Ensures that cyborg RCDs are spared.
var/obj/item/construction/rcd/RCD = I
RCD.detonate_pulse()
to_chat(owner, "<span class='danger'>RCD detonation pulse emitted.</span>")
owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0)
//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right?
/datum/AI_Module/large/mecha_domination
module_name = "Unlock Mech Domination"
mod_pick_name = "mechjack"
description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\
Do not allow the mech to leave the station's vicinity or allow it to be destroyed."
cost = 30
upgrade = TRUE
unlock_text = "<span class='notice'>Virus package compiled. Select a target mech at any time. <b>You must remain on the station at all times. Loss of signal will result in total system lockout.</b></span>"
unlock_sound = 'sound/mecha/nominal.ogg'
/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI)
AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk!
//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job.
/datum/AI_Module/large/break_fire_alarms
module_name = "Thermal Sensor Override"
mod_pick_name = "burnpigs"
description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \
Anyone can check the fire alarm's interface and may be tipped off by its status."
one_purchase = TRUE
cost = 25
power_type = /datum/action/innate/ai/break_fire_alarms
unlock_text = "<span class='notice'>You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will.</span>"
unlock_sound = 'goon/sound/machinery/firealarm.ogg'
/datum/action/innate/ai/break_fire_alarms
name = "Override Thermal Sensors"
desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless."
button_icon_state = "break_fire_alarms"
uses = 1
/datum/action/innate/ai/break_fire_alarms/Activate()
for(var/obj/machinery/firealarm/F in GLOB.machines)
if(!is_station_level(F.z))
continue
F.obj_flags |= EMAGGED
to_chat(owner, "<span class='notice'>All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.</span>")
owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0)
//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms.
/datum/AI_Module/large/break_air_alarms
module_name = "Air Alarm Safety Override"
mod_pick_name = "allow_flooding"
description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \
Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality."
one_purchase = TRUE
cost = 50
power_type = /datum/action/innate/ai/break_air_alarms
unlock_text = "<span class='notice'>You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard.</span>"
unlock_sound = 'sound/effects/space_wind.ogg'
/datum/action/innate/ai/break_air_alarms
name = "Override Air Alarm Safeties"
desc = "Enables the Flood setting on all air alarms."
button_icon_state = "break_air_alarms"
uses = 1
/datum/action/innate/ai/break_air_alarms/Activate()
for(var/obj/machinery/airalarm/AA in GLOB.machines)
if(!is_station_level(AA.z))
continue
AA.obj_flags |= EMAGGED
to_chat(owner, "<span class='notice'>All air alarm safeties on the station have been overridden. Air alarms may now use the Flood environmental mode.</span>")
owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0)
//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase.
/datum/AI_Module/small/overload_machine
module_name = "Machine Overload"
mod_pick_name = "overload"
description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase."
cost = 20
power_type = /datum/action/innate/ai/ranged/overload_machine
unlock_text = "<span class='notice'>You enable the ability for the station's APCs to direct intense energy into machinery.</span>"
unlock_sound = 'sound/effects/comfyfire.ogg' //definitely not comfy, but it's the closest sound to "roaring fire" we have
/datum/action/innate/ai/ranged/overload_machine
name = "Overload Machine"
desc = "Overheats a machine, causing a small explosion after a short time."
button_icon_state = "overload_machine"
uses = 2
linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine
/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M)
if(M && !QDELETED(M))
var/turf/T = get_turf(M)
message_admins("[ADMIN_LOOKUPFLW(usr)] overloaded [M.name] at [ADMIN_VERBOSEJMP(T)].")
log_game("[key_name(usr)] overloaded [M.name] at [AREACOORD(T)].")
explosion(get_turf(M), 0, 2, 3, 0)
if(M) //to check if the explosion killed it before we try to delete it
qdel(M)
/obj/effect/proc_holder/ranged_ai/overload_machine
active = FALSE
ranged_mousepointer = 'icons/effects/overload_machine_target.dmi'
enable_text = "<span class='notice'>You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel.</span>"
disable_text = "<span class='notice'>You release your hold on the powernet.</span>"
/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
if(!istype(target))
to_chat(ranged_ability_user, "<span class='warning'>You can only overload machines!</span>")
return
if(is_type_in_typecache(target, GLOB.blacklisted_malf_machines))
to_chat(ranged_ability_user, "<span class='warning'>You cannot overload that device!</span>")
return
ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0)
attached_action.adjust_uses(-1)
target.audible_message("<span class='userdanger'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom!
remove_ranged_ability("<span class='danger'>Overcharging machine...</span>")
return TRUE
//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself.
/datum/AI_Module/small/override_machine
module_name = "Machine Override"
mod_pick_name = "override"
description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses."
cost = 30
power_type = /datum/action/innate/ai/ranged/override_machine
unlock_text = "<span class='notice'>You procure a virus from the Space Dark Web and distribute it to the station's machines.</span>"
unlock_sound = 'sound/machines/airlock_alien_prying.ogg'
/datum/action/innate/ai/ranged/override_machine
name = "Override Machine"
desc = "Animates a targeted machine, causing it to attack anyone nearby."
button_icon_state = "override_machine"
uses = 4
linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine
/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M)
if(M && !QDELETED(M))
new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1)
/obj/effect/proc_holder/ranged_ai/override_machine
active = FALSE
ranged_mousepointer = 'icons/effects/override_machine_target.dmi'
enable_text = "<span class='notice'>You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel.</span>"
disable_text = "<span class='notice'>You release your hold on the powernet.</span>"
/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
if(!istype(target))
to_chat(ranged_ability_user, "<span class='warning'>You can only animate machines!</span>")
return
if(!target.can_be_overridden() || is_type_in_typecache(target, GLOB.blacklisted_malf_machines))
to_chat(ranged_ability_user, "<span class='warning'>That machine can't be overridden!</span>")
return
ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0)
attached_action.adjust_uses(-1)
target.audible_message("<span class='userdanger'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep!
remove_ranged_ability("<span class='danger'>Sending override signal...</span>")
return TRUE
//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting.
/datum/AI_Module/large/place_cyborg_transformer
module_name = "Robotic Factory (Removes Shunting)"
mod_pick_name = "cyborgtransformer"
description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside."
cost = 100
one_purchase = TRUE
power_type = /datum/action/innate/ai/place_transformer
unlock_text = "<span class='notice'>You make contact with Space Amazon and request a robotics factory for delivery.</span>"
unlock_sound = 'sound/machines/ping.ogg'
/datum/action/innate/ai/place_transformer
name = "Place Robotics Factory"
desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!"
button_icon_state = "robotic_factory"
uses = 1
auto_use_uses = FALSE //So we can attempt multiple times
var/list/turfOverlays
/datum/action/innate/ai/place_transformer/New()
..()
for(var/i in 1 to 3)
var/image/I = image("icon"='icons/turf/overlays.dmi')
LAZYADD(turfOverlays, I)
/datum/action/innate/ai/place_transformer/Activate()
if(!owner_AI.can_place_transformer(src))
return
active = TRUE
if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No")
active = FALSE
return
if(!owner_AI.can_place_transformer(src))
active = FALSE
return
var/turf/T = get_turf(owner_AI.eyeobj)
var/obj/machinery/transformer/conveyor = new(T)
conveyor.masterAI = owner
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
owner_AI.can_shunt = FALSE
to_chat(owner, "<span class='warning'>You are no longer able to shunt your core to APCs.</span>")
adjust_uses(-1)
/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T)
if(C && I.loc == T)
C.images -= I
/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action)
if(!eyeobj || !isturf(loc) || incapacitated() || !action)
return
var/turf/middle = get_turf(eyeobj)
var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z))
var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor."
var/success = TRUE
for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled
var/turf/T = turfs[n]
if(!isfloorturf(T))
success = FALSE
var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z)
if(!C.visibleTurfs[T])
alert_msg = "You don't have camera vision of this location!"
success = FALSE
for(var/atom/movable/AM in T.contents)
if(AM.density)
alert_msg = "That area must be clear of objects!"
success = FALSE
var/image/I = action.turfOverlays[n]
I.loc = T
client.images += I
I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively
addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30)
if(!success)
to_chat(src, "<span class='warning'>[alert_msg]</span>")
return success
//Blackout: Overloads a random number of lights across the station. Three uses.
/datum/AI_Module/small/blackout
module_name = "Blackout"
mod_pick_name = "blackout"
description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses."
cost = 15
power_type = /datum/action/innate/ai/blackout
unlock_text = "<span class='notice'>You hook into the powernet and route bonus power towards the station's lighting.</span>"
unlock_sound = "sparks"
/datum/action/innate/ai/blackout
name = "Blackout"
desc = "Overloads lights across the station."
button_icon_state = "blackout"
uses = 3
/datum/action/innate/ai/blackout/Activate()
for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
if(prob(30 * apc.overload))
apc.overload_lighting()
else
apc.overload++
to_chat(owner, "<span class='notice'>Overcurrent applied to the powernet.</span>")
owner.playsound_local(owner, "sparks", 50, 0)
//Disable Emergency Lights
/datum/AI_Module/small/emergency_lights
module_name = "Disable Emergency Lights"
mod_pick_name = "disable_emergency_lights"
description = "Cuts emergency lights across the entire station. If power is lost to light fixtures, they will not attempt to fall back on emergency power reserves."
cost = 10
one_purchase = TRUE
power_type = /datum/action/innate/ai/emergency_lights
unlock_text = "<span class='notice'>You hook into the powernet and locate the connections between light fixtures and their fallbacks.</span>"
unlock_sound = "sparks"
/datum/action/innate/ai/emergency_lights
name = "Disable Emergency Lights"
desc = "Disables all emergency lighting. Note that emergency lights can be restored through reboot at an APC."
button_icon_state = "emergency_lights"
uses = 1
/datum/action/innate/ai/emergency_lights/Activate()
for(var/obj/machinery/light/L in GLOB.machines)
if(is_station_level(L.z))
L.no_emergency = TRUE
INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE)
CHECK_TICK
to_chat(owner, "<span class='notice'>Emergency light connections severed.</span>")
owner.playsound_local(owner, 'sound/effects/light_flicker.ogg', 50, FALSE)
//Reactivate Camera Network: Reactivates up to 30 cameras across the station.
/datum/AI_Module/small/reactivate_cameras
module_name = "Reactivate Camera Network"
mod_pick_name = "recam"
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
cost = 10
one_purchase = TRUE
power_type = /datum/action/innate/ai/reactivate_cameras
unlock_text = "<span class='notice'>You deploy nanomachines to the cameranet.</span>"
unlock_sound = 'sound/items/wirecutter.ogg'
/datum/action/innate/ai/reactivate_cameras
name = "Reactivate Cameras"
desc = "Reactivates disabled cameras across the station; remaining uses can be used later."
button_icon_state = "reactivate_cameras"
uses = 30
auto_use_uses = FALSE
cooldown_period = 30
/datum/action/innate/ai/reactivate_cameras/New()
..()
desc = "[desc] There are 30 reactivations remaining."
button.desc = desc
/datum/action/innate/ai/reactivate_cameras/Activate()
var/fixed_cameras = 0
for(var/V in GLOB.cameranet.cameras)
if(!uses)
break
var/obj/machinery/camera/C = V
if(!C.status || C.view_range != initial(C.view_range))
C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc.
C.view_range = initial(C.view_range)
fixed_cameras++
uses-- //Not adjust_uses() so it doesn't automatically delete or show a message
to_chat(owner, "<span class='notice'>Diagnostic complete! Cameras reactivated: <b>[fixed_cameras]</b>. Reactivations remaining: <b>[uses]</b>.</span>")
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0)
adjust_uses(0, TRUE) //Checks the uses remaining
if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
desc = "[initial(desc)] There are [uses] reactivations remaining."
//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
/datum/AI_Module/large/upgrade_cameras
module_name = "Upgrade Camera Network"
mod_pick_name = "upgradecam"
description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble
//This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR.
one_purchase = TRUE
cost = 35 //Decent price for omniscience!
upgrade = TRUE
unlock_text = "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online.</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI)
AI.see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power.
AI.update_sight()
var/upgraded_cameras = 0
for(var/V in GLOB.cameranet.cameras)
var/obj/machinery/camera/C = V
if(C.assembly)
var/upgraded = 0
if(!C.isXRay())
C.upgradeXRay()
//Update what it can see.
GLOB.cameranet.updateVisibility(C, 0)
upgraded = 1
if(!C.isEmpProof())
C.upgradeEmpProof()
upgraded = 1
if(upgraded)
upgraded_cameras++
unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "<b>[upgraded_cameras]</b>") //This works, since unlock text is called after upgrade()
/datum/AI_Module/large/eavesdrop
module_name = "Enhanced Surveillance"
mod_pick_name = "eavesdrop"
description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations."
cost = 30
one_purchase = TRUE
upgrade = TRUE
unlock_text = "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online.</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI)
if(AI.eyeobj)
AI.eyeobj.relay_speech = TRUE
#undef DEFAULT_DOOMSDAY_TIMER
#undef DOOMSDAY_ANNOUNCE_INTERVAL
@@ -1,19 +0,0 @@
/datum/antagonist/heartbreaker
name = "heartbreaker"
roundend_category = "valentines"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
/datum/antagonist/heartbreaker/proc/forge_objectives()
var/datum/objective/martyr/normiesgetout = new
normiesgetout.owner = owner
objectives += normiesgetout
/datum/antagonist/heartbreaker/on_gain()
forge_objectives()
. = ..()
/datum/antagonist/heartbreaker/greet()
to_chat(owner, "<span class='warning'><B>You didn't get a date! They're all having fun without you! you'll show them though...</B></span>")
owner.announce_objectives()
@@ -1,62 +0,0 @@
/datum/antagonist/valentine
name = "valentine"
roundend_category = "valentines" //there's going to be a ton of them so put them in separate category
show_in_antagpanel = FALSE
var/datum/mind/date
/datum/antagonist/valentine/proc/forge_objectives()
var/datum/objective/protect/protect_objective = new /datum/objective/protect
protect_objective.owner = owner
protect_objective.target = date
if(!ishuman(date.current))
protect_objective.human_check = FALSE
protect_objective.explanation_text = "Protect [date.name], your date."
objectives += protect_objective
/datum/antagonist/valentine/on_gain()
forge_objectives()
if(isliving(owner))
var/mob/living/L = owner
L.apply_status_effect(STATUS_EFFECT_INLOVE, date)
. = ..()
/datum/antagonist/valentine/on_removal()
. = ..()
if(isliving(owner))
var/mob/living/L = owner
L.remove_status_effect(STATUS_EFFECT_INLOVE)
/datum/antagonist/valentine/greet()
to_chat(owner, "<span class='warning'><B>You're on a date with [date.name]! Protect [date.p_them()] at all costs. This takes priority over all other loyalties.</B></span>")
//Squashed up a bit
/datum/antagonist/valentine/roundend_report()
var/objectives_complete = TRUE
if(objectives.len)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
objectives_complete = FALSE
break
if(objectives_complete)
return "<span class='greentext big'>[owner.name] protected [owner.p_their()] date</span>"
else
return "<span class='redtext big'>[owner.name] date failed!</span>"
//Just so it's distinct, basically.
/datum/antagonist/valentine/chem/greet()
to_chat(owner, "<span class='warning'><B>You're in love with [date.name]! Protect [date.p_them()] at all costs. This takes priority over all other loyalties.</B></span>")
/datum/antagonist/valentine/chem/roundend_report()
var/objectives_complete = TRUE
if(objectives.len)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
objectives_complete = FALSE
break
if(objectives_complete)
return "<span class='greentext big'>[owner.name] protected [owner.p_their()] love: [date.name]! <i>What a cutie!</i></span>"
else
return "<span class='redtext big'>[owner.name] date failed!</span>"
@@ -1,29 +0,0 @@
/datum/antagonist/wishgranter
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new
hijack.owner = owner
objectives += hijack
/datum/antagonist/wishgranter/on_gain()
owner.special_role = "Avatar of the Wish Granter"
forge_objectives()
. = ..()
give_powers()
/datum/antagonist/wishgranter/greet()
to_chat(owner, "<B>Your inhibitions are swept away, the bonds of loyalty broken, you are free to murder as you please!</B>")
owner.announce_objectives()
/datum/antagonist/wishgranter/proc/give_powers()
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
H.dna.add_mutation(HULK)
H.dna.add_mutation(XRAY)
H.dna.add_mutation(COLDRES)
H.dna.add_mutation(TK)
@@ -1,445 +0,0 @@
//Apprenticeship contract - moved to antag_spawner.dm
///////////////////////////Veil Render//////////////////////
/obj/item/veilrender
name = "veil render"
desc = "A wicked curved blade of alien origin, recovered from the ruins of a vast city."
icon = 'icons/obj/wizard.dmi'
icon_state = "render"
item_state = "knife"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
force = 15
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
hitsound = 'sound/weapons/bladeslice.ogg'
var/charges = 1
var/spawn_type = /obj/singularity/wizard
var/spawn_amt = 1
var/activate_descriptor = "reality"
var/rend_desc = "You should run now."
var/spawn_fast = 0 //if 1, ignores checking for mobs on loc before spawning
/obj/item/veilrender/attack_self(mob/user)
if(charges > 0)
new /obj/effect/rend(get_turf(user), spawn_type, spawn_amt, rend_desc, spawn_fast)
charges--
user.visible_message("<span class='boldannounce'>[src] hums with power as [user] deals a blow to [activate_descriptor] itself!</span>")
else
to_chat(user, "<span class='danger'>The unearthly energies that powered the blade are now dormant.</span>")
/obj/effect/rend
name = "tear in the fabric of reality"
desc = "You should run now."
icon = 'icons/effects/effects.dmi'
icon_state = "rift"
density = TRUE
anchored = TRUE
var/spawn_path = /mob/living/simple_animal/cow //defaulty cows to prevent unintentional narsies
var/spawn_amt_left = 20
var/spawn_fast = 0
/obj/effect/rend/New(loc, var/spawn_type, var/spawn_amt, var/desc, var/spawn_fast)
src.spawn_path = spawn_type
src.spawn_amt_left = spawn_amt
src.desc = desc
src.spawn_fast = spawn_fast
START_PROCESSING(SSobj, src)
return
/obj/effect/rend/process()
if(!spawn_fast)
if(locate(/mob) in loc)
return
new spawn_path(loc)
spawn_amt_left--
if(spawn_amt_left <= 0)
qdel(src)
/obj/effect/rend/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/nullrod))
user.visible_message("<span class='danger'>[user] seals \the [src] with \the [I].</span>")
qdel(src)
return
else
return ..()
/obj/effect/rend/singularity_pull()
return
/obj/effect/rend/singularity_pull()
return
/obj/item/veilrender/vealrender
name = "veal render"
desc = "A wicked curved blade of alien origin, recovered from the ruins of a vast farm."
spawn_type = /mob/living/simple_animal/cow
spawn_amt = 20
activate_descriptor = "hunger"
rend_desc = "Reverberates with the sound of ten thousand moos."
/obj/item/veilrender/honkrender
name = "honk render"
desc = "A wicked curved blade of alien origin, recovered from the ruins of a vast circus."
spawn_type = /mob/living/simple_animal/hostile/retaliate/clown
spawn_amt = 10
activate_descriptor = "depression"
rend_desc = "Gently wafting with the sounds of endless laughter."
icon_state = "clownrender"
////TEAR IN REALITY
/obj/singularity/wizard
name = "tear in the fabric of reality"
desc = "This isn't right."
icon = 'icons/effects/224x224.dmi'
icon_state = "reality"
pixel_x = -96
pixel_y = -96
dissipate = 0
move_self = 0
consume_range = 3
grav_pull = 4
current_size = STAGE_FOUR
allowed_size = STAGE_FOUR
/obj/singularity/wizard/process()
move()
eat()
return
/obj/singularity/wizard/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
var/datum/component/mood/insaneinthemembrane = C.GetComponent(/datum/component/mood)
if(insaneinthemembrane.sanity < 15)
return //they've already seen it and are about to die, or are just too insane to care
to_chat(C, "<span class='userdanger'>OH GOD! NONE OF IT IS REAL! NONE OF IT IS REEEEEEEEEEEEEEEEEEEEEEEEAL!</span>")
insaneinthemembrane.sanity = 0
for(var/lore in typesof(/datum/brain_trauma/severe))
C.gain_trauma(lore)
addtimer(CALLBACK(src, /obj/singularity/wizard.proc/deranged, C), 100)
/obj/singularity/wizard/proc/deranged(mob/living/carbon/C)
if(!C || C.stat == DEAD)
return
C.vomit(0, TRUE, TRUE, 3, TRUE)
C.spew_organ(3, 2)
C.death()
/obj/singularity/wizard/mapped/admin_investigate_setup()
return
/////////////////////////////////////////Scrying///////////////////
/obj/item/scrying
name = "scrying orb"
desc = "An incandescent orb of otherworldly energy, staring into it gives you vision beyond mortal means."
icon = 'icons/obj/projectiles.dmi'
icon_state ="bluespace"
throw_speed = 3
throw_range = 7
throwforce = 15
damtype = BURN
force = 15
hitsound = 'sound/items/welder2.ogg'
var/xray_granted = FALSE
/obj/item/scrying/equipped(mob/user)
if(!xray_granted && ishuman(user))
var/mob/living/carbon/human/H = user
if(!(H.dna.check_mutation(XRAY)))
H.dna.add_mutation(XRAY)
xray_granted = TRUE
. = ..()
/obj/item/scrying/attack_self(mob/user)
to_chat(user, "<span class='notice'>You can see...everything!</span>")
visible_message("<span class='danger'>[user] stares into [src], their eyes glazing over.</span>")
user.ghostize(1)
/////////////////////////////////////////Necromantic Stone///////////////////
/obj/item/necromantic_stone
name = "necromantic stone"
desc = "A shard capable of resurrecting humans as skeleton thralls."
icon = 'icons/obj/wizard.dmi'
icon_state = "necrostone"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
var/list/spooky_scaries = list()
var/unlimited = 0
/obj/item/necromantic_stone/unlimited
unlimited = 1
/obj/item/necromantic_stone/attack(mob/living/carbon/human/M, mob/living/carbon/human/user)
if(!istype(M))
return ..()
if(!istype(user) || !user.canUseTopic(M, BE_CLOSE))
return
if(M.stat != DEAD)
to_chat(user, "<span class='warning'>This artifact can only affect the dead!</span>")
return
if(!M.mind || !M.client)
to_chat(user, "<span class='warning'>There is no soul connected to this body...</span>")
return
check_spooky()//clean out/refresh the list
if(spooky_scaries.len >= 3 && !unlimited)
to_chat(user, "<span class='warning'>This artifact can only affect three undead at a time!</span>")
return
M.set_species(/datum/species/skeleton/space, icon_update=0)
M.revive(full_heal = 1, admin_revive = 1)
spooky_scaries |= M
to_chat(M, "<span class='userdanger'>You have been revived by </span><B>[user.real_name]!</B>")
to_chat(M, "<span class='userdanger'>[user.p_theyre(TRUE)] your master now, assist [user.p_them()] even if it costs you your new life!</span>")
equip_roman_skeleton(M)
desc = "A shard capable of resurrecting humans as skeleton thralls[unlimited ? "." : ", [spooky_scaries.len]/3 active thralls."]"
/obj/item/necromantic_stone/proc/check_spooky()
if(unlimited) //no point, the list isn't used.
return
for(var/X in spooky_scaries)
if(!ishuman(X))
spooky_scaries.Remove(X)
continue
var/mob/living/carbon/human/H = X
if(H.stat == DEAD)
H.dust(TRUE)
spooky_scaries.Remove(X)
continue
listclearnulls(spooky_scaries)
//Funny gimmick, skeletons always seem to wear roman/ancient armour
/obj/item/necromantic_stone/proc/equip_roman_skeleton(mob/living/carbon/human/H)
for(var/obj/item/I in H)
H.dropItemToGround(I)
var/hat = pick(/obj/item/clothing/head/helmet/roman, /obj/item/clothing/head/helmet/roman/legionnaire)
H.equip_to_slot_or_del(new hat(H), SLOT_HEAD)
H.equip_to_slot_or_del(new /obj/item/clothing/under/roman(H), SLOT_W_UNIFORM)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/roman(H), SLOT_SHOES)
H.put_in_hands(new /obj/item/shield/riot/roman(H), TRUE)
H.put_in_hands(new /obj/item/claymore(H), TRUE)
H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), SLOT_BACK)
/obj/item/voodoo
name = "wicker doll"
desc = "Something creepy about it."
icon = 'icons/obj/wizard.dmi'
icon_state = "voodoo"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/mob/living/carbon/human/target = null
var/list/mob/living/carbon/human/possible = list()
var/obj/item/voodoo_link = null
var/cooldown_time = 30 //3s
var/cooldown = 0
max_integrity = 10
resistance_flags = FLAMMABLE
/obj/item/voodoo/attackby(obj/item/I, mob/user, params)
if(target && cooldown < world.time)
if(I.get_temperature())
to_chat(target, "<span class='userdanger'>You suddenly feel very hot</span>")
target.adjust_bodytemperature(50)
GiveHint(target)
else if(is_pointed(I))
to_chat(target, "<span class='userdanger'>You feel a stabbing pain in [parse_zone(user.zone_selected)]!</span>")
target.Knockdown(40)
GiveHint(target)
else if(istype(I, /obj/item/bikehorn))
to_chat(target, "<span class='userdanger'>HONK</span>")
SEND_SOUND(target, 'sound/items/airhorn.ogg')
target.adjustEarDamage(0,3)
GiveHint(target)
cooldown = world.time +cooldown_time
return
if(!voodoo_link)
if(I.loc == user && istype(I) && I.w_class <= WEIGHT_CLASS_SMALL)
if (user.transferItemToLoc(I,src))
voodoo_link = I
to_chat(user, "You attach [I] to the doll.")
update_targets()
/obj/item/voodoo/check_eye(mob/user)
if(loc != user)
user.reset_perspective(null)
user.unset_machine()
/obj/item/voodoo/attack_self(mob/user)
if(!target && possible.len)
target = input(user, "Select your victim!", "Voodoo") as null|anything in possible
return
if(user.zone_selected == BODY_ZONE_CHEST)
if(voodoo_link)
target = null
voodoo_link.forceMove(drop_location())
to_chat(user, "<span class='notice'>You remove the [voodoo_link] from the doll.</span>")
voodoo_link = null
update_targets()
return
if(target && cooldown < world.time)
switch(user.zone_selected)
if(BODY_ZONE_PRECISE_MOUTH)
var/wgw = sanitize(input(user, "What would you like the victim to say", "Voodoo", null) as text)
target.say(wgw, forced = "voodoo doll")
log_game("[key_name(user)] made [key_name(target)] say [wgw] with a voodoo doll.")
if(BODY_ZONE_PRECISE_EYES)
user.set_machine(src)
user.reset_perspective(target)
spawn(100)
user.reset_perspective(null)
user.unset_machine()
if(BODY_ZONE_R_LEG,BODY_ZONE_L_LEG)
to_chat(user, "<span class='notice'>You move the doll's legs around.</span>")
var/turf/T = get_step(target,pick(GLOB.cardinals))
target.Move(T)
if(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM)
target.click_random_mob()
GiveHint(target)
if(BODY_ZONE_HEAD)
to_chat(user, "<span class='notice'>You smack the doll's head with your hand.</span>")
target.Dizzy(10)
to_chat(target, "<span class='warning'>You suddenly feel as if your head was hit with a hammer!</span>")
GiveHint(target,user)
cooldown = world.time + cooldown_time
/obj/item/voodoo/proc/update_targets()
LAZYINITLIST(possible)
if(!voodoo_link)
return
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(md5(H.dna.uni_identity) in voodoo_link.fingerprints)
possible |= H
/obj/item/voodoo/proc/GiveHint(mob/victim,force=0)
if(prob(50) || force)
var/way = dir2text(get_dir(victim,get_turf(src)))
to_chat(victim, "<span class='notice'>You feel a dark presence from [way]</span>")
if(prob(20) || force)
var/area/A = get_area(src)
to_chat(victim, "<span class='notice'>You feel a dark presence from [A.name]</span>")
/obj/item/voodoo/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] links the voodoo doll to [user.p_them()]self and sits on it, infinitely crushing [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!</span>")
user.gib()
return(BRUTELOSS)
/obj/item/voodoo/fire_act(exposed_temperature, exposed_volume)
if(target)
target.adjust_fire_stacks(20)
target.IgniteMob()
GiveHint(target,1)
return ..()
//Provides a decent heal, need to pump every 6 seconds
/obj/item/organ/heart/cursed/wizard
pump_delay = 60
heal_brute = 25
heal_burn = 25
heal_oxy = 25
//Warp Whistle: Provides uncontrolled long distance teleportation.
/obj/item/warpwhistle
name = "warp whistle"
desc = "One toot on this whistle will send you to a far away land!"
icon = 'icons/obj/wizard.dmi'
icon_state = "whistle"
var/on_cooldown = 0 //0: usable, 1: in use, 2: on cooldown
var/mob/living/carbon/last_user
/obj/item/warpwhistle/proc/interrupted(mob/living/carbon/user)
if(!user || QDELETED(src) || user.notransform)
on_cooldown = FALSE
return TRUE
return FALSE
/obj/item/warpwhistle/proc/end_effect(mob/living/carbon/user)
user.invisibility = initial(user.invisibility)
user.status_flags &= ~GODMODE
user.canmove = TRUE
/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)
user.canmove = FALSE
new /obj/effect/temp_visual/tornado(T)
sleep(20)
if(interrupted(user))
return
user.invisibility = INVISIBILITY_MAXIMUM
user.status_flags |= GODMODE
sleep(20)
if(interrupted(user))
end_effect(user)
return
var/breakout = 0
while(breakout < 50)
if(!T)
end_effect(user)
return
var/turf/potential_T = find_safe_turf()
if(!potential_T)
end_effect(user)
return
if(T.z != potential_T.z || abs(get_dist_euclidian(potential_T,T)) > 50 - breakout)
do_teleport(user, potential_T, channel = TELEPORT_CHANNEL_MAGIC)
user.canmove = 0
T = potential_T
break
breakout += 1
new /obj/effect/temp_visual/tornado(T)
sleep(20)
end_effect(user)
if(interrupted(user))
return
on_cooldown = 2
sleep(40)
on_cooldown = 0
/obj/item/warpwhistle/Destroy()
if(on_cooldown == 1 && last_user) //Flute got dunked somewhere in the teleport
end_effect(last_user)
return ..()
/obj/effect/temp_visual/tornado
icon = 'icons/obj/wizard.dmi'
icon_state = "tornado"
name = "tornado"
desc = "This thing sucks!"
layer = FLY_LAYER
randomdir = 0
duration = 40
pixel_x = 500
/obj/effect/temp_visual/tornado/Initialize()
. = ..()
animate(src, pixel_x = -500, time = 40)
@@ -1,288 +0,0 @@
/obj/item/soulstone
name = "soulstone shard"
icon = 'icons/obj/wizard.dmi'
icon_state = "soulstone"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
layer = HIGH_OBJ_LAYER
desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefact's power."
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_BELT
var/usability = FALSE
var/old_shard = FALSE
var/spent = FALSE
/obj/item/soulstone/proc/was_used()
if(old_shard)
spent = TRUE
name = "dull [name]"
desc = "A fragment of the legendary treasure known simply as \
the 'Soul Stone'. The shard lies still, dull and lifeless; \
whatever spark it once held long extinguished."
/obj/item/soulstone/anybody
usability = TRUE
/obj/item/soulstone/anybody/chaplain
name = "mysterious old shard"
old_shard = TRUE
/obj/item/soulstone/pickup(mob/living/user)
..()
if(!iscultist(user) && !iswizard(user) && !usability)
to_chat(user, "<span class='danger'>An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.</span>")
/obj/item/soulstone/examine(mob/user)
..()
if(usability || iscultist(user) || iswizard(user) || isobserver(user))
if (old_shard)
to_chat(user, "<span class='cult'>A soulstone, used to capture a soul, either from dead humans or from freed shades.</span>")
else
to_chat(user, "<span class='cult'>A soulstone, used to capture souls, either from unconscious or sleeping humans or from freed shades.</span>")
to_chat(user, "<span class='cult'>The captured soul can be placed into a construct shell to produce a construct, or released from the stone as a shade.</span>")
if(spent)
to_chat(user, "<span class='cult'>This shard is spent; it is now just a creepy rock.</span>")
/obj/item/soulstone/Destroy() //Stops the shade from being qdel'd immediately and their ghost being sent back to the arrival shuttle.
for(var/mob/living/simple_animal/shade/A in src)
A.death()
return ..()
//////////////////////////////Capturing////////////////////////////////////////////////////////
/obj/item/soulstone/attack(mob/living/carbon/human/M, mob/living/user)
if(!iscultist(user) && !iswizard(user) && !usability)
user.Unconscious(100)
to_chat(user, "<span class='userdanger'>Your body is wracked with debilitating pain!</span>")
return
if(spent)
to_chat(user, "<span class='warning'>There is no power left in the shard.</span>")
return
if(!ishuman(M))//If target is not a human.
return ..()
if(iscultist(M))
if(iscultist(user))
to_chat(user, "<span class='cultlarge'>\"Come now, do not capture your bretheren's soul.\"</span>")
return
log_combat(user, M, "captured [M.name]'s soul", src)
transfer_soul("VICTIM", M, user)
///////////////////Options for using captured souls///////////////////////////////////////
/obj/item/soulstone/attack_self(mob/living/user)
if(!in_range(src, user))
return
if(!iscultist(user) && !iswizard(user) && !usability)
user.Unconscious(100)
to_chat(user, "<span class='userdanger'>Your body is wracked with debilitating pain!</span>")
return
release_shades(user)
/obj/item/soulstone/proc/release_shades(mob/user)
for(var/mob/living/simple_animal/shade/A in src)
A.status_flags &= ~GODMODE
A.canmove = TRUE
A.forceMove(get_turf(user))
A.cancel_camera()
icon_state = "soulstone"
name = initial(name)
if(iswizard(user) || usability)
to_chat(A, "<b>You have been released from your prison, but you are still bound to [user.real_name]'s will. Help [user.p_them()] succeed in [user.p_their()] goals at all costs.</b>")
else if(iscultist(user))
to_chat(A, "<b>You have been released from your prison, but you are still bound to the cult's will. Help them succeed in their goals at all costs.</b>")
was_used()
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
/obj/structure/constructshell
name = "empty shell"
icon = 'icons/obj/wizard.dmi'
icon_state = "construct-cult"
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
/obj/structure/constructshell/examine(mob/user)
..()
if(iscultist(user) || iswizard(user) || user.stat == DEAD)
to_chat(user, "<span class='cult'>A construct shell, used to house bound souls from a soulstone.</span>")
to_chat(user, "<span class='cult'>Placing a soulstone with a soul into this shell allows you to produce your choice of the following:</span>")
to_chat(user, "<span class='cult'>An <b>Artificer</b>, which can produce <b>more shells and soulstones</b>, as well as fortifications.</span>")
to_chat(user, "<span class='cult'>A <b>Wraith</b>, which does high damage and can jaunt through walls, though it is quite fragile.</span>")
to_chat(user, "<span class='cult'>A <b>Juggernaut</b>, which is very hard to kill and can produce temporary walls, but is slow.</span>")
/obj/structure/constructshell/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/soulstone))
var/obj/item/soulstone/SS = O
if(!iscultist(user) && !iswizard(user) && !SS.usability)
to_chat(user, "<span class='danger'>An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.</span>")
user.Dizzy(30)
return
SS.transfer_soul("CONSTRUCT",src,user)
SS.was_used()
else
return ..()
////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
/obj/item/soulstone/proc/transfer_soul(choice as text, target, mob/user)
switch(choice)
if("FORCE")
if(!iscarbon(target)) //TODO: Add sacrifice stoning for non-organics, just because you have no body doesnt mean you dont have a soul
return FALSE
if(contents.len)
return FALSE
var/mob/living/carbon/T = target
if(T.client != null)
for(var/obj/item/W in T)
T.dropItemToGround(W)
init_shade(T, user)
return TRUE
else
to_chat(user, "<span class='userdanger'>Capture failed!</span>: The soul has already fled its mortal frame. You attempt to bring it back...")
return getCultGhost(T,user)
if("VICTIM")
var/mob/living/carbon/human/T = target
var/datum/antagonist/cult/C = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(C && C.cult_team.is_sacrifice_target(T.mind))
if(iscultist(user))
to_chat(user, "<span class='cult'><b>\"This soul is mine.</b></span> <span class='cultlarge'>SACRIFICE THEM!\"</span>")
else
to_chat(user, "<span class='danger'>The soulstone seems to reject this soul.</span>")
return FALSE
if(contents.len)
to_chat(user, "<span class='userdanger'>Capture failed!</span>: The soulstone is full! Free an existing soul to make room.")
else
if((!old_shard && T.stat != CONSCIOUS) || (old_shard && T.stat == DEAD))
if(T.client == null)
to_chat(user, "<span class='userdanger'>Capture failed!</span>: The soul has already fled its mortal frame. You attempt to bring it back...")
getCultGhost(T,user)
else
for(var/obj/item/W in T)
T.dropItemToGround(W)
init_shade(T, user, vic = 1)
qdel(T)
else
to_chat(user, "<span class='userdanger'>Capture failed!</span>: Kill or maim the victim first!")
if("SHADE")
var/mob/living/simple_animal/shade/T = target
if(contents.len)
to_chat(user, "<span class='userdanger'>Capture failed!</span>: The soulstone is full! Free an existing soul to make room.")
else
T.forceMove(src) //put shade in stone
T.status_flags |= GODMODE
T.canmove = FALSE
T.health = T.maxHealth
icon_state = "soulstone2"
name = "soulstone: Shade of [T.real_name]"
to_chat(T, "<span class='notice'>Your soul has been captured by the soulstone. Its arcane energies are reknitting your ethereal form.</span>")
if(user != T)
to_chat(user, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been captured and stored within the soulstone.")
if("CONSTRUCT")
var/obj/structure/constructshell/T = target
var/mob/living/simple_animal/shade/A = locate() in src
if(A)
var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
if(!T || !T.loc)
return
switch(construct_class)
if("Juggernaut")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, A, user, 0, T.loc)
if("Wraith")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, A, user, 0, T.loc)
if("Artificer")
if(iscultist(user) || iswizard(user))
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, A, user, 0, T.loc)
else
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder/noncult, A, user, 0, T.loc)
for(var/datum/mind/B in SSticker.mode.cult)
if(B == A.mind)
SSticker.mode.cult -= A.mind
SSticker.mode.update_cult_icons_removed(A.mind)
qdel(T)
qdel(src)
else
to_chat(user, "<span class='userdanger'>Creation failed!</span>: The soul stone is empty! Go kill someone!")
/proc/makeNewConstruct(mob/living/simple_animal/hostile/construct/ctype, mob/target, mob/stoner = null, cultoverride = 0, loc_override = null)
var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target)))
if(stoner)
newstruct.faction |= "[REF(stoner)]"
newstruct.master = stoner
var/datum/action/innate/seek_master/SM = new()
SM.Grant(newstruct)
target.transfer_ckey(newstruct)
var/obj/screen/alert/bloodsense/BS
if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode)
SSticker.mode.add_cultist(newstruct.mind, 0)
if(iscultist(stoner) || cultoverride)
to_chat(newstruct, "<b>You are still bound to serve the cult[stoner ? " and [stoner]":""], follow [stoner ? stoner.p_their() : "their"] orders and help [stoner ? stoner.p_them() : "them"] complete [stoner ? stoner.p_their() : "their"] goals at all costs.</b>")
else if(stoner)
to_chat(newstruct, "<b>You are still bound to serve your creator, [stoner], follow [stoner.p_their()] orders and help [stoner.p_them()] complete [stoner.p_their()] goals at all costs.</b>")
newstruct.clear_alert("bloodsense")
BS = newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
if(BS)
BS.Cviewer = newstruct
newstruct.cancel_camera()
/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/U, vic = 0)
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
T.stop_sound_channel(CHANNEL_HEARTBEAT)
T.invisibility = INVISIBILITY_ABSTRACT
T.dust_animation()
QDEL_IN(T, 5)
var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade(src)
S.status_flags |= GODMODE //So they won't die inside the stone somehow
S.canmove = FALSE//Can't move out of the soul stone
S.name = "Shade of [T.real_name]"
S.real_name = "Shade of [T.real_name]"
T.transfer_ckey(S)
S.language_holder = U.language_holder.copy(S)
if(U)
S.faction |= "[REF(U)]" //Add the master as a faction, allowing inter-mob cooperation
if(U && iscultist(U))
SSticker.mode.add_cultist(S.mind, 0)
S.cancel_camera()
name = "soulstone: Shade of [T.real_name]"
icon_state = "soulstone2"
if(U && (iswizard(U) || usability))
to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help [U.p_them()] succeed in [U.p_their()] goals at all costs.")
else if(U && iscultist(U))
to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
if(vic && U)
to_chat(U, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been ripped from [T.p_their()] body and stored within the soul stone.")
/obj/item/soulstone/proc/getCultGhost(mob/living/carbon/human/T, mob/U)
var/mob/dead/observer/chosen_ghost
for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body
if(ghost.mind && ghost.mind.current == T && ghost.client)
chosen_ghost = ghost
break
if(!chosen_ghost) //Failing that, we grab a ghost
var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, poll_time = 50)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
return FALSE
if(!chosen_ghost)
to_chat(U, "<span class='danger'>There were no spirits willing to become a shade.</span>")
return FALSE
if(contents.len) //If they used the soulstone on someone else in the meantime
return FALSE
T.ckey = chosen_ghost.ckey
for(var/obj/item/W in T)
T.dropItemToGround(W)
init_shade(T, U)
qdel(T)
return TRUE
@@ -1,775 +0,0 @@
/datum/spellbook_entry
var/name = "Entry Name"
var/spell_type = null
var/desc = ""
var/category = "Offensive"
var/cost = 2
var/refundable = 1
var/surplus = -1 // -1 for infinite, not used by anything atm
var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell
var/buy_word = "Learn"
var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook
var/list/no_coexistance_typecache //Used so you can't have specific spells together
var/dynamic_cost = 0 // How much threat the spell costs to purchase for dynamic.
var/dynamic_requirement = 0 // How high the threat level needs to be for purchasing in dynamic.
/datum/spellbook_entry/New()
..()
no_coexistance_typecache = typecacheof(no_coexistance_typecache)
/datum/spellbook_entry/proc/IsAvailible() // For config prefs / gamemode restrictions - these are round applied
if(istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if(dynamic_requirement > 0 && mode.threat_level < dynamic_requirement)
return 0
return 1
/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user,obj/item/spellbook/book) // Specific circumstances
if(book.uses<cost || limit == 0)
return 0
for(var/spell in user.mind.spell_list)
if(is_type_in_typecache(spell, no_coexistance_typecache))
return 0
if(dynamic_cost>0 && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if(mode.threat < dynamic_cost)
return 0
return 1
/datum/spellbook_entry/proc/Buy(mob/living/carbon/human/user,obj/item/spellbook/book) //return 1 on success
if(!S || QDELETED(S))
S = new spell_type()
//Check if we got the spell already
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name)) // Not using directly in case it was learned from one spellbook then upgraded in another
if(aspell.spell_level >= aspell.level_max)
to_chat(user, "<span class='warning'>This spell cannot be improved further.</span>")
return 0
else
aspell.name = initial(aspell.name)
aspell.spell_level++
aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max)
if(aspell.charge_max < aspell.charge_counter)
aspell.charge_counter = aspell.charge_max
switch(aspell.spell_level)
if(1)
to_chat(user, "<span class='notice'>You have improved [aspell.name] into Efficient [aspell.name].</span>")
aspell.name = "Efficient [aspell.name]"
if(2)
to_chat(user, "<span class='notice'>You have further improved [aspell.name] into Quickened [aspell.name].</span>")
aspell.name = "Quickened [aspell.name]"
if(3)
to_chat(user, "<span class='notice'>You have further improved [aspell.name] into Free [aspell.name].</span>")
aspell.name = "Free [aspell.name]"
if(4)
to_chat(user, "<span class='notice'>You have further improved [aspell.name] into Instant [aspell.name].</span>")
aspell.name = "Instant [aspell.name]"
if(aspell.spell_level >= aspell.level_max)
to_chat(user, "<span class='notice'>This spell cannot be strengthened any further.</span>")
SSblackbox.record_feedback("nested tally", "wizard_spell_improved", 1, list("[name]", "[aspell.spell_level]"))
return 1
//No same spell found - just learn it
if(dynamic_cost > 0 && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.spend_threat(dynamic_cost)
mode.log_threat("Wizard spent [dynamic_cost] on [name].")
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
user.mind.AddSpell(S)
to_chat(user, "<span class='notice'>You have learned [S.name].</span>")
return 1
/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user,obj/item/spellbook/book)
if(!refundable)
return 0
if(!S)
S = new spell_type()
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name))
return 1
return 0
/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user,obj/item/spellbook/book) //return point value or -1 for failure
var/area/wizard_station/A = GLOB.areas_by_type[/area/wizard_station]
if(!(user in A.contents))
to_chat(user, "<span class='warning'>You can only refund spells at the wizard lair</span>")
return -1
if(!S)
S = new spell_type()
var/spell_levels = 0
if(dynamic_cost > 0 && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.refund_threat(dynamic_cost)
mode.log_threat("Wizard refunded [dynamic_cost] on [name].")
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name))
spell_levels = aspell.spell_level
user.mind.spell_list.Remove(aspell)
qdel(S)
return cost * (spell_levels+1)
return -1
/datum/spellbook_entry/proc/GetInfo()
if(!S)
S = new spell_type()
var/dat =""
dat += "<b>[initial(S.name)]</b>"
if(S.charge_type == "recharge")
dat += " Cooldown:[S.charge_max/10]"
dat += " Cost:[cost]<br>"
dat += "<i>[S.desc][desc]</i><br>"
dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]<br>"
return dat
/datum/spellbook_entry/fireball
name = "Fireball"
spell_type = /obj/effect/proc_holder/spell/aimed/fireball
/datum/spellbook_entry/spell_cards
name = "Spell Cards"
spell_type = /obj/effect/proc_holder/spell/aimed/spell_cards
/datum/spellbook_entry/rod_form
name = "Rod Form"
spell_type = /obj/effect/proc_holder/spell/targeted/rod_form
/datum/spellbook_entry/magicm
name = "Magic Missile"
spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile
category = "Defensive"
/datum/spellbook_entry/disintegrate
name = "Disintegrate"
spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate
/datum/spellbook_entry/disabletech
name = "Disable Tech"
spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech
category = "Defensive"
cost = 1
/datum/spellbook_entry/repulse
name = "Repulse"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse
category = "Defensive"
/datum/spellbook_entry/lightningPacket
name = "Lightning bolt! Lightning bolt!"
spell_type = /obj/effect/proc_holder/spell/targeted/conjure_item/spellpacket
category = "Defensive"
/datum/spellbook_entry/timestop
name = "Time Stop"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop
category = "Defensive"
/datum/spellbook_entry/smoke
name = "Smoke"
spell_type = /obj/effect/proc_holder/spell/targeted/smoke
category = "Defensive"
cost = 1
/datum/spellbook_entry/blind
name = "Blind"
spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind
cost = 1
/datum/spellbook_entry/mindswap
name = "Mindswap"
spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer
category = "Mobility"
/datum/spellbook_entry/forcewall
name = "Force Wall"
spell_type = /obj/effect/proc_holder/spell/targeted/forcewall
category = "Defensive"
cost = 1
/datum/spellbook_entry/blink
name = "Blink"
spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink
category = "Mobility"
/datum/spellbook_entry/teleport
name = "Teleport"
spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport
category = "Mobility"
/datum/spellbook_entry/mutate
name = "Mutate"
spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate
/datum/spellbook_entry/jaunt
name = "Ethereal Jaunt"
spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt
category = "Mobility"
/datum/spellbook_entry/knock
name = "Knock"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock
category = "Mobility"
cost = 1
/datum/spellbook_entry/fleshtostone
name = "Flesh to Stone"
spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone
/datum/spellbook_entry/summonitem
name = "Summon Item"
spell_type = /obj/effect/proc_holder/spell/targeted/summonitem
category = "Assistance"
cost = 1
/datum/spellbook_entry/lichdom
name = "Bind Soul"
spell_type = /obj/effect/proc_holder/spell/targeted/lichdom
category = "Defensive"
/datum/spellbook_entry/teslablast
name = "Tesla Blast"
spell_type = /obj/effect/proc_holder/spell/targeted/tesla
/datum/spellbook_entry/lightningbolt
name = "Lightning Bolt"
spell_type = /obj/effect/proc_holder/spell/aimed/lightningbolt
cost = 3
/datum/spellbook_entry/lightningbolt/Buy(mob/living/carbon/human/user,obj/item/spellbook/book) //return 1 on success
. = ..()
user.flags_1 |= TESLA_IGNORE_1
/datum/spellbook_entry/infinite_guns
name = "Lesser Summon Guns"
spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun
cost = 3
no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage
/datum/spellbook_entry/arcane_barrage
name = "Arcane Barrage"
spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage
cost = 3
no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun
/datum/spellbook_entry/barnyard
name = "Barnyard Curse"
spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse
/datum/spellbook_entry/charge
name = "Charge"
spell_type = /obj/effect/proc_holder/spell/targeted/charge
category = "Assistance"
cost = 1
/datum/spellbook_entry/shapeshift
name = "Wild Shapeshift"
spell_type = /obj/effect/proc_holder/spell/targeted/shapeshift
category = "Assistance"
cost = 1
/datum/spellbook_entry/spacetime_dist
name = "Spacetime Distortion"
spell_type = /obj/effect/proc_holder/spell/spacetime_dist
category = "Defensive"
cost = 1
/datum/spellbook_entry/the_traps
name = "The Traps!"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/the_traps
category = "Defensive"
cost = 1
/datum/spellbook_entry/item
name = "Buy Item"
refundable = 0
buy_word = "Summon"
var/item_path= null
/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
new item_path(get_turf(user))
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
return 1
/datum/spellbook_entry/item/GetInfo()
var/dat =""
dat += "<b>[name]</b>"
dat += " Cost:[cost]<br>"
dat += "<i>[desc]</i><br>"
if(surplus>=0)
dat += "[surplus] left.<br>"
return dat
/datum/spellbook_entry/item/staffchange
name = "Staff of Change"
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
item_path = /obj/item/gun/magic/staff/change
dynamic_requirement = 60
dynamic_cost = 20
/datum/spellbook_entry/item/staffanimation
name = "Staff of Animation"
desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines."
item_path = /obj/item/gun/magic/staff/animate
category = "Assistance"
/datum/spellbook_entry/item/staffchaos
name = "Staff of Chaos"
desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended."
item_path = /obj/item/gun/magic/staff/chaos
/datum/spellbook_entry/item/spellblade
name = "Spellblade"
desc = "A sword capable of firing blasts of energy which rip targets limb from limb."
item_path = /obj/item/gun/magic/staff/spellblade
/datum/spellbook_entry/item/staffdoor
name = "Staff of Door Creation"
desc = "A particular staff that can mold solid metal into ornate doors. Useful for getting around in the absence of other transportation. Does not work on glass."
item_path = /obj/item/gun/magic/staff/door
cost = 1
category = "Mobility"
/datum/spellbook_entry/item/staffhealing
name = "Staff of Healing"
desc = "An altruistic staff that can heal the lame and raise the dead."
item_path = /obj/item/gun/magic/staff/healing
cost = 1
category = "Defensive"
/datum/spellbook_entry/item/lockerstaff
name = "Staff of the Locker"
desc = "A staff that shoots lockers. It eats anyone it hits on its way, leaving a welded locker with your victims behind."
item_path = /obj/item/gun/magic/staff/locker
category = "Defensive"
/datum/spellbook_entry/item/scryingorb
name = "Scrying Orb"
desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you X-ray vision."
item_path = /obj/item/scrying
category = "Defensive"
/datum/spellbook_entry/item/soulstones
name = "Six Soul Stone Shards and the spell Artificer"
desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot."
item_path = /obj/item/storage/belt/soulstone/full
category = "Assistance"
/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
. =..()
if(.)
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null))
return .
/datum/spellbook_entry/item/necrostone
name = "A Necromantic Stone"
desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command."
item_path = /obj/item/necromantic_stone
category = "Assistance"
/datum/spellbook_entry/item/wands
name = "Wand Assortment"
desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt."
item_path = /obj/item/storage/belt/wands/full
category = "Defensive"
/datum/spellbook_entry/item/armor
name = "Mastercrafted Armor Set"
desc = "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space."
item_path = /obj/item/clothing/suit/space/hardsuit/wizard
category = "Defensive"
/datum/spellbook_entry/item/armor/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
. = ..()
if(.)
new /obj/item/clothing/shoes/sandal/magic(get_turf(user)) //In case they've lost them.
new /obj/item/clothing/gloves/color/purple(get_turf(user))//To complete the outfit
/datum/spellbook_entry/item/contract
name = "Contract of Apprenticeship"
desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side."
item_path = /obj/item/antag_spawner/contract
category = "Assistance"
dynamic_requirement = 50
dynamic_cost = 10
/datum/spellbook_entry/item/guardian
name = "Guardian Deck"
desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \
It would be wise to avoid buying these with anything capable of causing you to swap bodies with others."
item_path = /obj/item/guardiancreator/choose/wizard
category = "Assistance"
/datum/spellbook_entry/item/guardian/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
. = ..()
if(.)
new /obj/item/paper/guides/antag/guardian/wizard(get_turf(user))
/datum/spellbook_entry/item/bloodbottle
name = "Bottle of Blood"
desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim."
item_path = /obj/item/antag_spawner/slaughter_demon
limit = 3
category = "Assistance"
dynamic_requirement = 60
/datum/spellbook_entry/item/bloodbottle/New()
..()
dynamic_cost = CONFIG_GET(keyed_list/dynamic_cost)["slaughter_demon"]
/datum/spellbook_entry/item/hugbottle
name = "Bottle of Tickles"
desc = "A bottle of magically infused fun, the smell of which will \
attract adorable extradimensional beings when broken. These beings \
are similar to slaughter demons, but they do not permamently kill \
their victims, instead putting them in an extradimensional hugspace, \
to be released on the demon's death. Chaotic, but not ultimately \
damaging. The crew's reaction to the other hand could be very \
destructive."
item_path = /obj/item/antag_spawner/slaughter_demon/laughter
cost = 1 //non-destructive; it's just a jape, sibling!
limit = 3
category = "Assistance"
dynamic_requirement = 40
/datum/spellbook_entry/item/hugbottle/New()
..()
dynamic_cost = CONFIG_GET(keyed_list/dynamic_cost)["slaughter_demon"]/3
/datum/spellbook_entry/item/mjolnir
name = "Mjolnir"
desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power."
item_path = /obj/item/twohanded/mjollnir
/datum/spellbook_entry/item/singularity_hammer
name = "Singularity Hammer"
desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact."
item_path = /obj/item/twohanded/singularityhammer
/datum/spellbook_entry/item/battlemage
name = "Battlemage Armour"
desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted."
item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard
limit = 1
category = "Defensive"
/datum/spellbook_entry/item/battlemage_charge
name = "Battlemage Armour Charges"
desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour."
item_path = /obj/item/wizard_armour_charge
category = "Defensive"
cost = 1
/datum/spellbook_entry/item/warpwhistle
name = "Warp Whistle"
desc = "A strange whistle that will transport you to a distant safe place on the station. There is a window of vulnerability at the beginning of every use."
item_path = /obj/item/warpwhistle
category = "Mobility"
cost = 1
/datum/spellbook_entry/summon
name = "Summon Stuff"
category = "Rituals"
refundable = 0
buy_word = "Cast"
var/active = 0
/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user,obj/item/spellbook/book)
return ..() && !active
/datum/spellbook_entry/summon/GetInfo()
var/dat =""
dat += "<b>[name]</b>"
if(cost>0)
dat += " Cost:[cost]<br>"
else
dat += " No Cost<br>"
dat += "<i>[desc]</i><br>"
if(active)
dat += "<b>Already cast!</b><br>"
return dat
/datum/spellbook_entry/summon/ghosts
name = "Summon Ghosts"
desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you."
cost = 0
/datum/spellbook_entry/summon/ghosts/IsAvailible()
if(!SSticker.mode)
return FALSE
else
return ..()
/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
new /datum/round_event/wizard/ghost()
active = TRUE
to_chat(user, "<span class='notice'>You have cast summon ghosts!</span>")
playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1)
return TRUE
/datum/spellbook_entry/summon/guns
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!"
dynamic_cost = 10
dynamic_requirement = 60
/datum/spellbook_entry/summon/guns/IsAvailible()
if(!SSticker.mode) // In case spellbook is placed on map
return 0
return (!CONFIG_GET(flag/no_summon_guns) && ..())
/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
rightandwrong(SUMMON_GUNS, user, 25)
active = 1
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "<span class='notice'>You have cast summon guns!</span>")
if(istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
var/threat_spent = dynamic_cost
mode.spend_threat(threat_spent)
mode.log_threat("Wizard spent [threat_spent] on summon guns.")
return 1
/datum/spellbook_entry/summon/magic
name = "Summon Magic"
desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time."
dynamic_cost = 10
dynamic_requirement = 60
/datum/spellbook_entry/summon/magic/IsAvailible()
if(!SSticker.mode) // In case spellbook is placed on map
return 0
return (!CONFIG_GET(flag/no_summon_guns) && ..())
/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
rightandwrong(SUMMON_MAGIC, user, 25)
active = 1
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "<span class='notice'>You have cast summon magic!</span>")
if(istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
var/threat_spent = dynamic_cost
mode.spend_threat(threat_spent)
mode.log_threat("Wizard spent [threat_spent] on summon magic.")
return 1
/datum/spellbook_entry/summon/events
name = "Summon Events"
desc = "Give Murphy's law a little push and replace all events with special wizard ones that will confound and confuse everyone. Multiple castings increase the rate of these events."
dynamic_cost = 20
dynamic_requirement = 60
var/times = 0
/datum/spellbook_entry/summon/events/IsAvailible()
if(!SSticker.mode) // In case spellbook is placed on map
return 0
return (!CONFIG_GET(flag/no_summon_events) && ..())
/datum/spellbook_entry/summon/events/Buy(mob/living/carbon/human/user,obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
summonevents()
if(istype(SSticker.mode,/datum/game_mode/dynamic) && times == 0)
var/datum/game_mode/dynamic/mode = SSticker.mode
var/threat_spent = dynamic_cost
mode.spend_threat(threat_spent)
mode.log_threat("Wizard spent [threat_spent] on summon events.")
times++
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "<span class='notice'>You have cast summon events.</span>")
return 1
/datum/spellbook_entry/summon/events/GetInfo()
. = ..()
if(times>0)
. += "You cast it [times] times.<br>"
return .
/obj/item/spellbook
name = "spell book"
desc = "An unearthly tome that glows with power."
icon = 'icons/obj/library.dmi'
icon_state ="book"
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_TINY
var/uses = 10
var/temp = null
var/tab = null
var/mob/living/carbon/human/owner
var/list/datum/spellbook_entry/entries = list()
var/list/categories = list()
/obj/item/spellbook/examine(mob/user)
..()
if(owner)
to_chat(user, "There is a small signature on the front cover: \"[owner]\".")
else
to_chat(user, "It appears to have no author.")
/obj/item/spellbook/Initialize()
. = ..()
prepare_spells()
/obj/item/spellbook/proc/prepare_spells()
var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon
for(var/T in entry_types)
var/datum/spellbook_entry/E = new T
if(E.IsAvailible())
entries |= E
categories |= E.category
else
qdel(E)
tab = categories[1]
/obj/item/spellbook/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/antag_spawner/contract))
var/obj/item/antag_spawner/contract/contract = O
if(contract.used)
to_chat(user, "<span class='warning'>The contract has been used, you can't get your points back now!</span>")
else
to_chat(user, "<span class='notice'>You feed the contract back into the spellbook, refunding your points.</span>")
uses++
for(var/datum/spellbook_entry/item/contract/CT in entries)
if(!isnull(CT.limit))
CT.limit++
qdel(O)
else if(istype(O, /obj/item/antag_spawner/slaughter_demon))
to_chat(user, "<span class='notice'>On second thought, maybe summoning a demon is a bad idea. You refund your points.</span>")
uses++
for(var/datum/spellbook_entry/item/bloodbottle/BB in entries)
if(!isnull(BB.limit))
BB.limit++
qdel(O)
/obj/item/spellbook/proc/GetCategoryHeader(category)
var/dat = ""
switch(category)
if("Offensive")
dat += "Spells and items geared towards debilitating and destroying.<BR><BR>"
dat += "Items are not bound to you and can be stolen. Additionally they cannot typically be returned once purchased.<BR>"
dat += "For spells: the number after the spell name is the cooldown time.<BR>"
dat += "You can reduce this number by spending more points on the spell.<BR>"
if("Defensive")
dat += "Spells and items geared towards improving your survivability or reducing foes' ability to attack.<BR><BR>"
dat += "Items are not bound to you and can be stolen. Additionally they cannot typically be returned once purchased.<BR>"
dat += "For spells: the number after the spell name is the cooldown time.<BR>"
dat += "You can reduce this number by spending more points on the spell.<BR>"
if("Mobility")
dat += "Spells and items geared towards improving your ability to move. It is a good idea to take at least one.<BR><BR>"
dat += "Items are not bound to you and can be stolen. Additionally they cannot typically be returned once purchased.<BR>"
dat += "For spells: the number after the spell name is the cooldown time.<BR>"
dat += "You can reduce this number by spending more points on the spell.<BR>"
if("Assistance")
dat += "Spells and items geared towards bringing in outside forces to aid you or improving upon your other items and abilities.<BR><BR>"
dat += "Items are not bound to you and can be stolen. Additionally they cannot typically be returned once purchased.<BR>"
dat += "For spells: the number after the spell name is the cooldown time.<BR>"
dat += "You can reduce this number by spending more points on the spell.<BR>"
if("Challenges")
dat += "The Wizard Federation typically has hard limits on the potency and number of spells brought to the station based on risk.<BR>"
dat += "Arming the station against you will increases the risk, but will grant you one more charge for your spellbook.<BR>"
if("Rituals")
dat += "These powerful spells change the very fabric of reality. Not always in your favour.<BR>"
return dat
/obj/item/spellbook/proc/wrap(content)
var/dat = ""
dat +="<html><head><title>Spellbook</title></head>"
dat += {"
<head>
<style type="text/css">
body { font-size: 80%; font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; }
ul#tabs { list-style-type: none; margin: 30px 0 0 0; padding: 0 0 0.3em 0; }
ul#tabs li { display: inline; }
ul#tabs li a { color: #42454a; background-color: #dedbde; border: 1px solid #c9c3ba; border-bottom: none; padding: 0.3em; text-decoration: none; }
ul#tabs li a:hover { background-color: #f1f0ee; }
ul#tabs li a.selected { color: #000; background-color: #f1f0ee; font-weight: bold; padding: 0.7em 0.3em 0.38em 0.3em; }
div.tabContent { border: 1px solid #c9c3ba; padding: 0.5em; background-color: #f1f0ee; }
div.tabContent.hide { display: none; }
</style>
</head>
"}
dat += {"[content]</body></html>"}
return dat
/obj/item/spellbook/attack_self(mob/user)
if(!owner)
to_chat(user, "<span class='notice'>You bind the spellbook to yourself.</span>")
owner = user
return
if(user != owner)
to_chat(user, "<span class='warning'>The [name] does not recognize you as its owner and refuses to open!</span>")
return
user.set_machine(src)
var/dat = ""
dat += "<ul id=\"tabs\">"
var/list/cat_dat = list()
for(var/category in categories)
cat_dat[category] = "<hr>"
dat += "<li><a [tab==category?"class=selected":""] href='byond://?src=[REF(src)];page=[category]'>[category]</a></li>"
dat += "<li><a><b>Points remaining : [uses]</b></a></li>"
dat += "</ul>"
var/datum/spellbook_entry/E
for(var/i=1,i<=entries.len,i++)
var/spell_info = ""
E = entries[i]
spell_info += E.GetInfo()
if(E.CanBuy(user,src))
spell_info+= "<a href='byond://?src=[REF(src)];buy=[i]'>[E.buy_word]</A><br>"
else
spell_info+= "<span>Can't [E.buy_word]</span><br>"
if(E.CanRefund(user,src))
spell_info+= "<a href='byond://?src=[REF(src)];refund=[i]'>Refund</A><br>"
spell_info += "<hr>"
if(cat_dat[E.category])
cat_dat[E.category] += spell_info
for(var/category in categories)
dat += "<div class=\"[tab==category?"tabContent":"tabContent hide"]\" id=\"[category]\">"
dat += GetCategoryHeader(category)
dat += cat_dat[category]
dat += "</div>"
user << browse(wrap(dat), "window=spellbook;size=700x500")
onclose(user, "spellbook")
return
/obj/item/spellbook/Topic(href, href_list)
..()
var/mob/living/carbon/human/H = usr
if(H.stat || H.restrained())
return
if(!ishuman(H))
return 1
if(H.mind.special_role == "apprentice")
temp = "If you got caught sneaking a peek from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not."
return
var/datum/spellbook_entry/E = null
if(loc == H || (in_range(src, H) && isturf(loc)))
H.set_machine(src)
if(href_list["buy"])
E = entries[text2num(href_list["buy"])]
if(E && E.CanBuy(H,src))
if(E.Buy(H,src))
if(E.limit)
E.limit--
uses -= E.cost
else if(href_list["refund"])
E = entries[text2num(href_list["refund"])]
if(E && E.refundable)
var/result = E.Refund(H,src)
if(result > 0)
if(!isnull(E.limit))
E.limit += result
uses += result
else if(href_list["page"])
tab = sanitize(href_list["page"])
attack_self(H)
return
-295
View File
@@ -1,295 +0,0 @@
/datum/antagonist/wizard
name = "Space Wizard"
roundend_category = "wizards/witches"
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
antag_moodlet = /datum/mood_event/focused
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
var/hud_version = "wizard"
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wizard/on_gain()
register()
equip_wizard()
if(give_objectives)
create_objectives()
if(move_to_lair)
send_to_lair()
. = ..()
if(allow_rename)
rename_wizard()
/datum/antagonist/wizard/proc/register()
SSticker.mode.wizards |= owner
/datum/antagonist/wizard/proc/unregister()
SSticker.mode.wizards -= src
/datum/antagonist/wizard/create_team(datum/team/wizard/new_team)
if(!new_team)
return
if(!istype(new_team))
stack_trace("Wrong team type passed to [type] initialization.")
wiz_team = new_team
/datum/antagonist/wizard/get_team()
return wiz_team
/datum/team/wizard
name = "wizard team"
var/datum/antagonist/wizard/master_wizard
/datum/antagonist/wizard/proc/create_wiz_team()
wiz_team = new(owner)
wiz_team.name = "[owner.current.real_name] team"
wiz_team.master_wizard = src
update_wiz_icons_added(owner.current)
/datum/antagonist/wizard/proc/send_to_lair()
if(!owner || !owner.current)
return
if(!GLOB.wizardstart.len)
SSjob.SendToLateJoin(owner.current)
to_chat(owner, "HOT INSERTION, GO GO GO")
else
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/new_objective = new("Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.")
new_objective.completed = TRUE //So they can greentext without admin intervention.
new_objective.owner = owner
objectives += new_objective
if (!(locate(/datum/objective/escape) in objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = owner
objectives += escape_objective
/datum/antagonist/wizard/on_removal()
unregister()
owner.RemoveAllSpells() // TODO keep track which spells are wizard spells which innate stuff
return ..()
/datum/antagonist/wizard/proc/equip_wizard()
if(!owner)
return
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
if(strip)
H.delete_equipment()
//Wizards are human by default. Use the mirror if you want something else.
H.set_species(/datum/species/human)
if(H.age < wiz_age)
H.age = wiz_age
H.equipOutfit(outfit_type)
/datum/antagonist/wizard/greet()
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
owner.announce_objectives()
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
to_chat(owner,"<B>Remember:</B> do not forget to prepare your spells.")
/datum/antagonist/wizard/farewell()
to_chat(owner, "<span class='userdanger'>You have been brainwashed! You are no longer a wizard!</span>")
/datum/antagonist/wizard/proc/rename_wizard()
set waitfor = FALSE
var/wizard_name_first = pick(GLOB.wizard_first)
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
var/newname = copytext(sanitize(input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
if (!newname)
newname = randomname
wiz_mob.fully_replace_character_name(wiz_mob.real_name, newname)
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
M.faction |= ROLE_WIZARD
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_removed(M)
M.faction -= ROLE_WIZARD
/datum/antagonist/wizard/get_admin_commands()
. = ..()
.["Send to Lair"] = CALLBACK(src,.proc/admin_send_to_lair)
/datum/antagonist/wizard/proc/admin_send_to_lair(mob/admin)
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/apprentice
name = "Wizard Apprentice"
hud_version = "apprentice"
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
wiz_age = APPRENTICE_AGE_MIN
/datum/antagonist/wizard/apprentice/greet()
to_chat(owner, "<B>You are [master.current.real_name]'s apprentice! You are bound by magic contract to follow [master.p_their()] orders and help [master.p_them()] in accomplishing [master.p_their()] goals.")
owner.announce_objectives()
/datum/antagonist/wizard/apprentice/register()
SSticker.mode.apprentices |= owner
/datum/antagonist/wizard/apprentice/unregister()
SSticker.mode.apprentices -= owner
/datum/antagonist/wizard/apprentice/equip_wizard()
. = ..()
if(!owner)
return
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
switch(school)
if(APPRENTICE_DESTRUCTION)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.")
if(APPRENTICE_BLUESPACE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
if(APPRENTICE_HEALING)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
/datum/antagonist/wizard/apprentice/create_objectives()
var/datum/objective/protect/new_objective = new /datum/objective/protect
new_objective.owner = owner
new_objective.target = master
new_objective.explanation_text = "Protect [master.current.real_name], the wizard."
objectives += new_objective
//Random event wizard
/datum/antagonist/wizard/apprentice/imposter
name = "Wizard Imposter"
allow_rename = FALSE
move_to_lair = FALSE
/datum/antagonist/wizard/apprentice/imposter/greet()
to_chat(owner, "<B>You are an imposter! Trick and confuse the crew to misdirect malice from your handsome original!</B>")
owner.announce_objectives()
/datum/antagonist/wizard/apprentice/imposter/equip_wizard()
var/mob/living/carbon/human/master_mob = master.current
var/mob/living/carbon/human/H = owner.current
if(!istype(master_mob) || !istype(H))
return
if(master_mob.ears)
H.equip_to_slot_or_del(new master_mob.ears.type, SLOT_EARS)
if(master_mob.w_uniform)
H.equip_to_slot_or_del(new master_mob.w_uniform.type, SLOT_W_UNIFORM)
if(master_mob.shoes)
H.equip_to_slot_or_del(new master_mob.shoes.type, SLOT_SHOES)
if(master_mob.wear_suit)
H.equip_to_slot_or_del(new master_mob.wear_suit.type, SLOT_WEAR_SUIT)
if(master_mob.head)
H.equip_to_slot_or_del(new master_mob.head.type, SLOT_HEAD)
if(master_mob.back)
H.equip_to_slot_or_del(new master_mob.back.type, SLOT_BACK)
//Operation: Fuck off and scare people
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz,join = TRUE)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.join_hud(wiz)
set_antag_hud(wiz, hud_version)
/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.leave_hud(wiz)
set_antag_hud(wiz, null)
/datum/antagonist/wizard/academy
name = "Academy Teacher"
outfit_type = /datum/outfit/wizard/academy
/datum/antagonist/wizard/academy/equip_wizard()
. = ..()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile)
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball)
var/mob/living/M = owner.current
if(!istype(M))
return
var/obj/item/implant/exile/Implant = new
Implant.implant(M)
/datum/antagonist/wizard/academy/create_objectives()
var/datum/objective/new_objective = new("Protect Wizard Academy from the intruders")
new_objective.owner = owner
objectives += new_objective
//Solo wizard report
/datum/antagonist/wizard/roundend_report()
var/list/parts = list()
parts += printplayer(owner)
var/count = 1
var/wizardwin = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = 0
count++
if(wizardwin)
parts += "<span class='greentext'>The wizard was successful!</span>"
else
parts += "<span class='redtext'>The wizard has failed!</span>"
if(owner.spell_list.len>0)
parts += "<B>[owner.name] used the following spells: </B>"
var/list/spell_names = list()
for(var/obj/effect/proc_holder/spell/S in owner.spell_list)
spell_names += S.name
parts += spell_names.Join(", ")
return parts.Join("<br>")
//Wizard with apprentices report
/datum/team/wizard/roundend_report()
var/list/parts = list()
parts += "<span class='header'>Wizards/witches of [master_wizard.owner.name] team were:</span>"
parts += master_wizard.roundend_report()
parts += " "
parts += "<span class='header'>[master_wizard.owner.name] apprentices were:</span>"
parts += printplayerlist(members - master_wizard.owner)
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
-365
View File
@@ -1,365 +0,0 @@
#define CONFUSION_STACK_MAX_MULTIPLIER 2
/obj/item/assembly/flash
name = "flash"
desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production."
icon_state = "flash"
item_state = "flashtool"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
throwforce = 0
w_class = WEIGHT_CLASS_TINY
materials = list(MAT_METAL = 300, MAT_GLASS = 300)
crit_fail = FALSE //Is the flash burnt out?
light_color = LIGHT_COLOR_WHITE
light_power = FLASH_LIGHT_POWER
var/flashing_overlay = "flash-f"
var/times_used = 0 //Number of times it's been used.
var/burnout_resistance = 0
var/last_used = 0 //last world.time it was used.
var/cooldown = 0
var/last_trigger = 0 //Last time it was successfully triggered.
/obj/item/assembly/flash/suicide_act(mob/living/user)
if (crit_fail)
user.visible_message("<span class='suicide'>[user] raises \the [src] up to [user.p_their()] eyes and activates it ... but its burnt out!</span>")
return SHAME
else if (user.eye_blind)
user.visible_message("<span class='suicide'>[user] raises \the [src] up to [user.p_their()] eyes and activates it ... but [user.p_theyre()] blind!</span>")
return SHAME
user.visible_message("<span class='suicide'>[user] raises \the [src] up to [user.p_their()] eyes and activates it! It looks like [user.p_theyre()] trying to commit suicide!</span>")
attack(user,user)
return FIRELOSS
/obj/item/assembly/flash/update_icon(flash = FALSE)
cut_overlays()
attached_overlays = list()
if(crit_fail)
add_overlay("flashburnt")
attached_overlays += "flashburnt"
if(flash)
add_overlay(flashing_overlay)
attached_overlays += flashing_overlay
addtimer(CALLBACK(src, .proc/update_icon), 5)
if(holder)
holder.update_icon()
/obj/item/assembly/flash/proc/clown_check(mob/living/carbon/human/user)
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
flash_carbon(user, user, 15, 0)
return FALSE
return TRUE
/obj/item/assembly/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something.
if(!crit_fail)
crit_fail = TRUE
update_icon()
if(ismob(loc))
var/mob/M = loc
M.visible_message("<span class='danger'>[src] burns out!</span>","<span class='userdanger'>[src] burns out!</span>")
else
var/turf/T = get_turf(src)
T.visible_message("<span class='danger'>[src] burns out!</span>")
/obj/item/assembly/flash/proc/flash_recharge(interval = 10)
var/deciseconds_passed = world.time - last_used
for(var/seconds = deciseconds_passed / 10, seconds >= interval, seconds -= interval) //get 1 charge every interval
times_used--
last_used = world.time
times_used = max(0, times_used) //sanity
if(max(0, prob(times_used * 3) - burnout_resistance)) //The more often it's used in a short span of time the more likely it will burn out
burn_out()
return FALSE
return TRUE
//BYPASS CHECKS ALSO PREVENTS BURNOUT!
/obj/item/assembly/flash/proc/AOE_flash(bypass_checks = FALSE, range = 3, power = 5, targeted = FALSE, mob/user)
if(!bypass_checks && !try_use_flash())
return FALSE
var/list/mob/targets = get_flash_targets(get_turf(src), range, FALSE)
if(user)
targets -= user
for(var/mob/living/carbon/C in targets)
flash_carbon(C, user, power, targeted, TRUE)
return TRUE
/obj/item/assembly/flash/proc/get_flash_targets(atom/target_loc, range = 3, override_vision_checks = FALSE)
if(!target_loc)
target_loc = loc
if(override_vision_checks)
return get_hearers_in_view(range, get_turf(target_loc))
if(isturf(target_loc) || (ismob(target_loc) && isturf(target_loc.loc)))
return viewers(range, get_turf(target_loc))
else
return typecache_filter_list(target_loc.GetAllContents(), GLOB.typecache_living)
/obj/item/assembly/flash/proc/try_use_flash(mob/user = null)
if(crit_fail || (world.time < last_trigger + cooldown))
return FALSE
last_trigger = world.time
playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
flash_lighting_fx(FLASH_LIGHT_RANGE, light_power, light_color)
times_used++
flash_recharge()
update_icon(TRUE)
if(user && !clown_check(user))
return FALSE
return TRUE
/obj/item/assembly/flash/proc/flash_carbon(mob/living/carbon/M, mob/user, power = 15, targeted = TRUE, generic_message = FALSE)
if(!istype(M))
return
if(user)
log_combat(user, M, "[targeted? "flashed(targeted)" : "flashed(AOE)"]", src)
else //caused by emp/remote signal
M.log_message("was [targeted? "flashed(targeted)" : "flashed(AOE)"]",LOG_ATTACK)
if(generic_message && M != user)
to_chat(M, "<span class='disarm'>[src] emits a blinding light!</span>")
if(targeted)
if(M.flash_act(1, 1))
if(M.confused < power)
var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
M.confused += min(power, diff)
if(user)
terrible_conversion_proc(M, user)
visible_message("<span class='disarm'>[user] blinds [M] with the flash!</span>")
to_chat(user, "<span class='danger'>You blind [M] with the flash!</span>")
to_chat(M, "<span class='userdanger'>[user] blinds you with the flash!</span>")
else
to_chat(M, "<span class='userdanger'>You are blinded by [src]!</span>")
var/toblur = 20 - M.eye_blurry
if(toblur > 0)
M.blur_eyes(toblur)
else if(user)
visible_message("<span class='disarm'>[user] fails to blind [M] with the flash!</span>")
to_chat(user, "<span class='warning'>You fail to blind [M] with the flash!</span>")
to_chat(M, "<span class='danger'>[user] fails to blind you with the flash!</span>")
else
to_chat(M, "<span class='danger'>[src] fails to blind you!</span>")
else
if(M.flash_act())
var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
M.confused += min(power, diff)
/obj/item/assembly/flash/attack(mob/living/M, mob/user)
if(!try_use_flash(user))
return FALSE
if(iscarbon(M))
flash_carbon(M, user, 20, 1)
return TRUE
else if(issilicon(M))
var/mob/living/silicon/robot/R = M
log_combat(user, R, "flashed", src)
update_icon(1)
R.Knockdown(rand(80,120))
var/diff = 5 * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
R.confused += min(5, diff)
R.flash_act(affect_silicon = 1)
user.visible_message("<span class='disarm'>[user] overloads [R]'s sensors with the flash!</span>", "<span class='danger'>You overload [R]'s sensors with the flash!</span>")
return TRUE
user.visible_message("<span class='disarm'>[user] fails to blind [M] with the flash!</span>", "<span class='warning'>You fail to blind [M] with the flash!</span>")
/obj/item/assembly/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0)
if(holder)
return FALSE
if(!AOE_flash(FALSE, 3, 5, FALSE, user))
return FALSE
to_chat(user, "<span class='danger'>[src] emits a blinding light!</span>")
/obj/item/assembly/flash/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
if(!try_use_flash())
return
AOE_flash()
burn_out()
/obj/item/assembly/flash/activate()//AOE flash on signal received
if(!..())
return
AOE_flash()
/obj/item/assembly/flash/proc/terrible_conversion_proc(mob/living/carbon/human/H, mob/user)
if(istype(H) && ishuman(user) && H.stat != DEAD)
if(user.mind)
var/datum/antagonist/rev/head/converter = user.mind.has_antag_datum(/datum/antagonist/rev/head)
if(!converter)
return
if(!H.client)
to_chat(user, "<span class='warning'>This mind is so vacant that it is not susceptible to influence!</span>")
return
if(H.stat != CONSCIOUS)
to_chat(user, "<span class='warning'>They must be conscious before you can convert [H.p_them()]!</span>")
return
if(converter.add_revolutionary(H.mind))
times_used -- //Flashes less likely to burn out for headrevs when used for conversion
else
to_chat(user, "<span class='warning'>This mind seems resistant to the flash!</span>")
/obj/item/assembly/flash/cyborg
/obj/item/assembly/flash/cyborg/attack(mob/living/M, mob/user)
. = ..()
new /obj/effect/temp_visual/borgflash(get_turf(src))
if(. && !CONFIG_GET(flag/disable_borg_flash_knockdown) && iscarbon(M) && !M.resting && !M.get_eye_protection())
M.Knockdown(80)
/obj/item/assembly/flash/cyborg/attack_self(mob/user)
..()
new /obj/effect/temp_visual/borgflash(get_turf(src))
/obj/item/assembly/flash/cyborg/attackby(obj/item/W, mob/user, params)
return
/obj/item/assembly/flash/cyborg/screwdriver_act(mob/living/user, obj/item/I)
return
/obj/item/assembly/flash/memorizer
name = "memorizer"
desc = "If you see this, you're not likely to remember it any time soon."
icon = 'icons/obj/device.dmi'
icon_state = "memorizer"
item_state = "nullrod"
/obj/item/assembly/flash/handheld //this is now the regular pocket flashes
/obj/item/assembly/flash/armimplant
name = "photon projector"
desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out."
var/flashcd = 20
var/overheat = 0
var/obj/item/organ/cyberimp/arm/flash/I = null
/obj/item/assembly/flash/armimplant/burn_out()
if(I && I.owner)
to_chat(I.owner, "<span class='warning'>Your photon projector implant overheats and deactivates!</span>")
I.Retract()
overheat = TRUE
addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2)
/obj/item/assembly/flash/armimplant/try_use_flash(mob/user = null)
if(overheat)
if(I && I.owner)
to_chat(I.owner, "<span class='warning'>Your photon projector is running too hot to be used again so quickly!</span>")
return FALSE
overheat = TRUE
addtimer(CALLBACK(src, .proc/cooldown), flashcd)
playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
update_icon(1)
return TRUE
/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
materials = list(MAT_GLASS=7500, MAT_METAL=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, .proc/update_icon), 5)
if(holder)
holder.update_icon()
/obj/item/assembly/flash/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
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."
flashing_overlay = "flash-hypno" //I cannot find this icon no matter how hard I look in tg, so I might just make my own.
light_color = LIGHT_COLOR_PINK
cooldown = 20
/obj/item/assembly/flash/hypnotic/burn_out()
return
/obj/item/assembly/flash/hypnotic/flash_carbon(mob/living/carbon/M, mob/user, power = 15, targeted = TRUE, generic_message = FALSE)
if(!istype(M))
return
if(user)
log_combat(user, M, "[targeted? "hypno-flashed(targeted)" : "hypno-flashed(AOE)"]", src)
else //caused by emp/remote signal
M.log_message("was [targeted? "hypno-flashed(targeted)" : "hypno-flashed(AOE)"]",LOG_ATTACK)
if(generic_message && M != user)
to_chat(M, "<span class='disarm'>[src] emits a soothing light...</span>")
if(targeted)
if(M.flash_act(1, 1))
var/hypnosis = FALSE
if(M.hypnosis_vulnerable())
hypnosis = TRUE
if(user)
user.visible_message("<span class='disarm'>[user] blinds [M] with the flash!</span>", "<span class='danger'>You hypno-flash [M]!</span>")
if(!hypnosis)
to_chat(M, "<span class='notice'>The light makes you feel oddly relaxed...</span>")
M.confused += min(M.confused + 10, 20)
M.dizziness += min(M.dizziness + 10, 20)
M.drowsyness += min(M.drowsyness + 10, 20)
M.apply_status_effect(STATUS_EFFECT_PACIFY, 100)
else
M.apply_status_effect(/datum/status_effect/trance, 200, TRUE)
else if(user)
user.visible_message("<span class='disarm'>[user] fails to blind [M] with the flash!</span>", "<span class='warning'>You fail to hypno-flash [M]!</span>")
else
to_chat(M, "<span class='danger'>[src] fails to blind you!</span>")
else if(M.flash_act())
to_chat(M, "<span class='notice'>Such a pretty light...</span>")
M.confused += min(M.confused + 4, 20)
M.dizziness += min(M.dizziness + 4, 20)
M.drowsyness += min(M.drowsyness + 4, 20)
-239
View File
@@ -1,239 +0,0 @@
/obj/item/assembly/infra
name = "infrared emitter"
desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
icon_state = "infrared"
materials = list(MAT_METAL=1000, MAT_GLASS=500)
is_position_sensitive = TRUE
var/on = FALSE
var/visible = FALSE
var/maxlength = 8
var/list/obj/effect/beam/i_beam/beams
var/olddir = 0
var/turf/listeningTo
var/hearing_range = 3
/obj/item/assembly/infra/Initialize()
. = ..()
beams = list()
START_PROCESSING(SSobj, src)
/obj/item/assembly/infra/ComponentInitialize()
. = ..()
AddComponent(
/datum/component/simple_rotation,
ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS,
null,
null,
CALLBACK(src,.proc/after_rotation)
)
/obj/item/assembly/infra/proc/after_rotation()
refreshBeam()
/obj/item/assembly/infra/Destroy()
STOP_PROCESSING(SSobj, src)
listeningTo = null
QDEL_LIST(beams)
. = ..()
/obj/item/assembly/infra/examine(mob/user)
..()
to_chat(user, "<span class='notice'>The infrared trigger is [on?"on":"off"].</span>")
/obj/item/assembly/infra/activate()
if(!..())
return FALSE//Cooldown check
on = !on
refreshBeam()
update_icon()
return TRUE
/obj/item/assembly/infra/toggle_secure()
secured = !secured
if(secured)
START_PROCESSING(SSobj, src)
refreshBeam()
else
QDEL_LIST(beams)
STOP_PROCESSING(SSobj, src)
update_icon()
return secured
/obj/item/assembly/infra/update_icon()
cut_overlays()
attached_overlays = list()
if(on)
add_overlay("infrared_on")
attached_overlays += "infrared_on"
if(visible && secured)
add_overlay("infrared_visible")
attached_overlays += "infrared_visible"
if(holder)
holder.update_icon()
return
/obj/item/assembly/infra/dropped()
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
else
refreshBeam()
/obj/item/assembly/infra/process()
if(!on || !secured)
refreshBeam()
return
/obj/item/assembly/infra/proc/refreshBeam()
QDEL_LIST(beams)
if(throwing || !on || !secured)
return
if(holder)
if(holder.master) //incase the sensor is part of an assembly that's contained in another item, such as a single tank bomb
if(!holder.master.IsSpecialAssembly() || !isturf(holder.master.loc))
return
else if(!isturf(holder.loc)) //else just check where the holder is
return
else if(!isturf(loc)) //or just where the fuck we are in general
return
var/turf/T = get_turf(src)
var/_dir = dir
var/turf/_T = get_step(T, _dir)
if(_T)
for(var/i in 1 to maxlength)
var/obj/effect/beam/i_beam/I = new(T)
if(istype(holder, /obj/item/assembly_holder))
var/obj/item/assembly_holder/assembly_holder = holder
I.icon_state = "[initial(I.icon_state)]_[(assembly_holder.a_left == src) ? "l":"r"]" //Sync the offset of the beam with the position of the sensor.
else if(istype(holder, /obj/item/transfer_valve))
I.icon_state = "[initial(I.icon_state)]_ttv"
I.density = TRUE
if(!I.Move(_T))
qdel(I)
switchListener(_T)
break
I.density = FALSE
beams += I
I.master = src
I.setDir(_dir)
I.invisibility = visible? 0 : INVISIBILITY_ABSTRACT
T = _T
_T = get_step(_T, _dir)
CHECK_TICK
/obj/item/assembly/infra/on_detach()
. = ..()
if(!.)
return
refreshBeam()
/obj/item/assembly/infra/attack_hand()
. = ..()
refreshBeam()
/obj/item/assembly/infra/Moved()
var/t = dir
. = ..()
setDir(t)
/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
. = ..()
olddir = dir
/obj/item/assembly/infra/throw_impact()
. = ..()
if(!olddir)
return
setDir(olddir)
olddir = null
/obj/item/assembly/infra/proc/trigger_beam(atom/movable/AM, turf/location)
refreshBeam()
switchListener(location)
if(!secured || !on || next_activate > world.time)
return FALSE
pulse(FALSE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
if(ismob(CHM))
var/mob/LM = CHM
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
next_activate = world.time + 30
/obj/item/assembly/infra/proc/switchListener(turf/newloc)
if(listeningTo == newloc)
return
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_ATOM_EXITED)
RegisterSignal(newloc, COMSIG_ATOM_EXITED, .proc/check_exit)
listeningTo = newloc
/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/offender)
if(QDELETED(src))
return
if(offender == src || istype(offender,/obj/effect/beam/i_beam))
return
if (offender && isitem(offender))
var/obj/item/I = offender
if (I.item_flags & ABSTRACT)
return
return refreshBeam()
/obj/item/assembly/infra/ui_interact(mob/user)//TODO: change this this to the wire control panel
. = ..()
if(is_secured(user))
user.set_machine(src)
var/dat = "<TT><B>Infrared Laser</B></TT>"
dat += "<BR><B>Status</B>: [on ? "<A href='?src=[REF(src)];state=0'>On</A>" : "<A href='?src=[REF(src)];state=1'>Off</A>"]"
dat += "<BR><B>Visibility</B>: [visible ? "<A href='?src=[REF(src)];visible=0'>Visible</A>" : "<A href='?src=[REF(src)];visible=1'>Invisible</A>"]"
dat += "<BR><BR><A href='?src=[REF(src)];refresh=1'>Refresh</A>"
dat += "<BR><BR><A href='?src=[REF(src)];close=1'>Close</A>"
user << browse(dat, "window=infra")
onclose(user, "infra")
return
/obj/item/assembly/infra/Topic(href, href_list)
..()
if(usr.incapacitated() || !in_range(loc, usr))
usr << browse(null, "window=infra")
onclose(usr, "infra")
return
if(href_list["state"])
on = !(on)
update_icon()
refreshBeam()
if(href_list["visible"])
visible = !(visible)
update_icon()
refreshBeam()
if(href_list["close"])
usr << browse(null, "window=infra")
return
if(usr)
attack_self(usr)
/obj/item/assembly/infra/setDir()
. = ..()
refreshBeam()
/***************************IBeam*********************************/
/obj/effect/beam/i_beam
name = "infrared beam"
icon = 'icons/obj/projectiles.dmi'
icon_state = "ibeam"
var/obj/item/assembly/infra/master
anchored = TRUE
density = FALSE
pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
if(istype(AM, /obj/effect/beam))
return
if (isitem(AM))
var/obj/item/I = AM
if (I.item_flags & ABSTRACT)
return
master.trigger_beam(AM, get_turf(src))
@@ -1,259 +0,0 @@
/atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
return null
/turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
return
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
var/datum/gas_mixture/air_contents = return_air()
if(!air_contents)
return 0
var/oxy = air_contents.gases[/datum/gas/oxygen]
var/tox = air_contents.gases[/datum/gas/plasma]
var/trit = air_contents.gases[/datum/gas/tritium]
if(active_hotspot)
if(soh)
if((tox > 0.5 || trit > 0.5) && oxy > 0.5)
if(active_hotspot.temperature < exposed_temperature*50)
active_hotspot.temperature = exposed_temperature*50
if(active_hotspot.volume < exposed_volume)
active_hotspot.volume = exposed_volume
return 1
var/igniting = 0
if((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && (tox > 0.5 || trit > 0.5))
igniting = 1
if(igniting)
if(oxy < 0.5)
return 0
active_hotspot = new /obj/effect/hotspot(src)
active_hotspot.temperature = exposed_temperature*50
active_hotspot.volume = exposed_volume*25
active_hotspot.just_spawned = (current_cycle < SSair.times_fired)
//remove just_spawned protection if no longer processing this cell
SSair.add_to_active(src, 0)
else
var/datum/gas_mixture/heating = air_contents.remove_ratio(exposed_volume/air_contents.volume)
heating.temperature = exposed_temperature
heating.react()
assume_air(heating)
air_update_turf()
return igniting
//This is the icon for fire on turfs, also helps for nurturing small fires until they are full tile
/obj/effect/hotspot
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
icon = 'icons/effects/fire.dmi'
icon_state = "1"
layer = GASFIRE_LAYER
light_range = LIGHT_RANGE_FIRE
light_color = LIGHT_COLOR_FIRE
blend_mode = BLEND_ADD
var/volume = 125
var/temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
var/just_spawned = TRUE
var/bypassing = FALSE
var/visual_update_tick = 0
/obj/effect/hotspot/Initialize()
. = ..()
SSair.hotspots += src
perform_exposure()
setDir(pick(GLOB.cardinals))
air_update_turf()
/obj/effect/hotspot/proc/perform_exposure()
var/turf/open/location = loc
if(!istype(location) || !(location.air))
return
location.active_hotspot = src
if(volume > CELL_VOLUME*0.95)
bypassing = TRUE
else
bypassing = FALSE
if(bypassing)
if(!just_spawned)
volume = location.air.reaction_results["fire"]*FIRE_GROWTH_RATE
temperature = location.air.temperature
else
var/datum/gas_mixture/affected = location.air.remove_ratio(volume/location.air.volume)
affected.temperature = temperature
affected.react(src)
temperature = affected.temperature
volume = affected.reaction_results["fire"]*FIRE_GROWTH_RATE
location.assume_air(affected)
for(var/A in location)
var/atom/AT = A
if(!QDELETED(AT) && AT != src) // It's possible that the item is deleted in temperature_expose
AT.fire_act(temperature, volume)
return
/obj/effect/hotspot/proc/gauss_lerp(x, x1, x2)
var/b = (x1 + x2) * 0.5
var/c = (x2 - x1) / 6
return NUM_E ** -((x - b) ** 2 / (2 * c) ** 2)
/obj/effect/hotspot/proc/update_color()
cut_overlays()
var/heat_r = heat2colour_r(temperature)
var/heat_g = heat2colour_g(temperature)
var/heat_b = heat2colour_b(temperature)
var/heat_a = 255
var/greyscale_fire = 1 //This determines how greyscaled the fire is.
if(temperature < 5000) //This is where fire is very orange, we turn it into the normal fire texture here.
var/normal_amt = gauss_lerp(temperature, 1000, 3000)
heat_r = LERP(heat_r,255,normal_amt)
heat_g = LERP(heat_g,255,normal_amt)
heat_b = LERP(heat_b,255,normal_amt)
heat_a -= gauss_lerp(temperature, -5000, 5000) * 128
greyscale_fire -= normal_amt
if(temperature > 40000) //Past this temperature the fire will gradually turn a bright purple
var/purple_amt = temperature < LERP(40000,200000,0.5) ? gauss_lerp(temperature, 40000, 200000) : 1
heat_r = LERP(heat_r,255,purple_amt)
if(temperature > 200000 && temperature < 500000) //Somewhere at this temperature nitryl happens.
var/sparkle_amt = gauss_lerp(temperature, 200000, 500000)
var/mutable_appearance/sparkle_overlay = mutable_appearance('icons/effects/effects.dmi', "shieldsparkles")
sparkle_overlay.blend_mode = BLEND_ADD
sparkle_overlay.alpha = sparkle_amt * 255
add_overlay(sparkle_overlay)
if(temperature > 400000 && temperature < 1500000) //Lightning because very anime.
var/mutable_appearance/lightning_overlay = mutable_appearance(icon, "overcharged")
lightning_overlay.blend_mode = BLEND_ADD
add_overlay(lightning_overlay)
if(temperature > 4500000) //This is where noblium happens. Some fusion-y effects.
var/fusion_amt = temperature < LERP(4500000,12000000,0.5) ? gauss_lerp(temperature, 4500000, 12000000) : 1
var/mutable_appearance/fusion_overlay = mutable_appearance('icons/effects/atmospherics.dmi', "fusion_gas")
fusion_overlay.blend_mode = BLEND_ADD
fusion_overlay.alpha = fusion_amt * 255
var/mutable_appearance/rainbow_overlay = mutable_appearance('icons/mob/screen_gen.dmi', "druggy")
rainbow_overlay.blend_mode = BLEND_ADD
rainbow_overlay.alpha = fusion_amt * 255
rainbow_overlay.appearance_flags = RESET_COLOR
heat_r = LERP(heat_r,150,fusion_amt)
heat_g = LERP(heat_g,150,fusion_amt)
heat_b = LERP(heat_b,150,fusion_amt)
add_overlay(fusion_overlay)
add_overlay(rainbow_overlay)
set_light(l_color = rgb(LERP(250,heat_r,greyscale_fire),LERP(160,heat_g,greyscale_fire),LERP(25,heat_b,greyscale_fire)))
heat_r /= 255
heat_g /= 255
heat_b /= 255
color = list(LERP(0.3, 1, 1-greyscale_fire) * heat_r,0.3 * heat_g * greyscale_fire,0.3 * heat_b * greyscale_fire, 0.59 * heat_r * greyscale_fire,LERP(0.59, 1, 1-greyscale_fire) * heat_g,0.59 * heat_b * greyscale_fire, 0.11 * heat_r * greyscale_fire,0.11 * heat_g * greyscale_fire,LERP(0.11, 1, 1-greyscale_fire) * heat_b, 0,0,0)
alpha = heat_a
#define INSUFFICIENT(path) (location.air.gases[path] < 0.5)
/obj/effect/hotspot/process()
if(just_spawned)
just_spawned = FALSE
return
var/turf/open/location = loc
if(!istype(location))
qdel(src)
return
if(location.excited_group)
location.excited_group.reset_cooldowns()
if((temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) || (volume <= 1))
qdel(src)
return
if(!location.air || (INSUFFICIENT(/datum/gas/plasma) && INSUFFICIENT(/datum/gas/tritium)) || INSUFFICIENT(/datum/gas/oxygen))
qdel(src)
return
//Not enough to burn
if((location.air.gases[/datum/gas/plasma] < 0.5 && location.air.gases[/datum/gas/tritium] < 0.5) || location.air.gases[/datum/gas/oxygen] < 0.5)
qdel(src)
return
perform_exposure()
if(bypassing)
icon_state = "3"
location.burn_tile()
//Possible spread due to radiated heat
if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD)
var/radiated_temperature = location.air.temperature*FIRE_SPREAD_RADIOSITY_SCALE
for(var/t in location.atmos_adjacent_turfs)
var/turf/open/T = t
if(!T.active_hotspot)
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4)
else
if(volume > CELL_VOLUME*0.4)
icon_state = "2"
else
icon_state = "1"
if((visual_update_tick++ % 7) == 0)
update_color()
if(temperature > location.max_fire_temperature_sustained)
location.max_fire_temperature_sustained = temperature
if(location.heat_capacity && temperature > location.heat_capacity)
location.to_be_destroyed = TRUE
return TRUE
/obj/effect/hotspot/Destroy()
set_light(0)
SSair.hotspots -= src
var/turf/open/T = loc
if(istype(T) && T.active_hotspot == src)
T.active_hotspot = null
DestroyTurf()
return ..()
/obj/effect/hotspot/proc/DestroyTurf()
if(isturf(loc))
var/turf/T = loc
if(T.to_be_destroyed)
var/chance_of_deletion
if (T.heat_capacity) //beware of division by zero
chance_of_deletion = T.max_fire_temperature_sustained / T.heat_capacity * 8 //there is no problem with prob(23456), min() was redundant --rastaf0
else
chance_of_deletion = 100
if(prob(chance_of_deletion))
T.Melt()
else
T.to_be_destroyed = FALSE
T.max_fire_temperature_sustained = 0
/obj/effect/hotspot/Crossed(atom/movable/AM, oldLoc)
..()
if(isliving(AM))
var/mob/living/L = AM
L.fire_act(temperature, volume)
/obj/effect/hotspot/singularity_pull()
return
/obj/effect/dummy/lighting_obj/moblight/fire
name = "fire"
light_color = LIGHT_COLOR_FIRE
light_range = LIGHT_RANGE_FIRE
#undef INSUFFICIENT
@@ -1,133 +0,0 @@
/atom/var/CanAtmosPass = ATMOS_PASS_YES
/atom/var/CanAtmosPassVertical = ATMOS_PASS_YES
/atom/proc/CanAtmosPass(turf/T)
switch (CanAtmosPass)
if (ATMOS_PASS_PROC)
return ATMOS_PASS_YES
if (ATMOS_PASS_DENSITY)
return !density
else
return CanAtmosPass
/turf/CanAtmosPass = ATMOS_PASS_NO
/turf/CanAtmosPassVertical = ATMOS_PASS_NO
/turf/open/CanAtmosPass = ATMOS_PASS_PROC
/turf/open/CanAtmosPassVertical = ATMOS_PASS_PROC
/turf/open/CanAtmosPass(turf/T, vertical = FALSE)
var/dir = vertical? get_dir_multiz(src, T) : get_dir(src, T)
var/opp = dir_inverse_multiz(dir)
var/R = FALSE
if(vertical && !(zAirOut(dir, T) && T.zAirIn(dir, src)))
R = TRUE
if(blocks_air || T.blocks_air)
R = TRUE
if (T == src)
return !R
for(var/obj/O in contents+T.contents)
var/turf/other = (O.loc == src ? T : src)
if(!(vertical? (CANVERTICALATMOSPASS(O, other)) : (CANATMOSPASS(O, other))))
R = TRUE
if(O.BlockSuperconductivity()) //the direction and open/closed are already checked on CanAtmosPass() so there are no arguments
atmos_supeconductivity |= dir
T.atmos_supeconductivity |= opp
return FALSE //no need to keep going, we got all we asked
atmos_supeconductivity &= ~dir
T.atmos_supeconductivity &= ~opp
return !R
/atom/movable/proc/BlockSuperconductivity() // objects that block air and don't let superconductivity act. Only firelocks atm.
return FALSE
/turf/proc/ImmediateCalculateAdjacentTurfs()
var/canpass = CANATMOSPASS(src, src)
var/canvpass = CANVERTICALATMOSPASS(src, src)
for(var/direction in GLOB.cardinals_multiz)
var/turf/T = get_step_multiz(src, direction)
if(!isopenturf(T))
continue
if(!(blocks_air || T.blocks_air) && ((direction & (UP|DOWN))? (canvpass && CANVERTICALATMOSPASS(T, src)) : (canpass && CANATMOSPASS(T, src))) )
LAZYINITLIST(atmos_adjacent_turfs)
LAZYINITLIST(T.atmos_adjacent_turfs)
atmos_adjacent_turfs[T] = TRUE
T.atmos_adjacent_turfs[src] = TRUE
else
if (atmos_adjacent_turfs)
atmos_adjacent_turfs -= T
if (T.atmos_adjacent_turfs)
T.atmos_adjacent_turfs -= src
UNSETEMPTY(T.atmos_adjacent_turfs)
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
//returns a list of adjacent turfs that can share air with this one.
//alldir includes adjacent diagonal tiles that can share
// air with both of the related adjacent cardinal tiles
/turf/proc/GetAtmosAdjacentTurfs(alldir = 0)
var/adjacent_turfs
if (atmos_adjacent_turfs)
adjacent_turfs = atmos_adjacent_turfs.Copy()
else
adjacent_turfs = list()
if (!alldir)
return adjacent_turfs
var/turf/curloc = src
for (var/direction in GLOB.diagonals_multiz)
var/matchingDirections = 0
var/turf/S = get_step_multiz(curloc, direction)
if(!S)
continue
for (var/checkDirection in GLOB.cardinals_multiz)
var/turf/checkTurf = get_step(S, checkDirection)
if(!S.atmos_adjacent_turfs || !S.atmos_adjacent_turfs[checkTurf])
continue
if (adjacent_turfs[checkTurf])
matchingDirections++
if (matchingDirections >= 2)
adjacent_turfs += S
break
return adjacent_turfs
/atom/proc/air_update_turf(command = 0)
if(!isturf(loc) && command)
return
var/turf/T = get_turf(loc)
T.air_update_turf(command)
/turf/air_update_turf(command = 0)
if(command)
ImmediateCalculateAdjacentTurfs()
SSair.add_to_active(src,command)
/atom/movable/proc/move_update_air(turf/T)
if(isturf(T))
T.air_update_turf(1)
air_update_turf(1)
/atom/proc/atmos_spawn_air(text) //because a lot of people loves to copy paste awful code lets just make an easy proc to spawn your plasma fires
var/turf/open/T = get_turf(src)
if(!istype(T))
return
T.atmos_spawn_air(text)
/turf/open/atmos_spawn_air(text)
if(!text || !air)
return
var/datum/gas_mixture/G = new
G.parse_gas_string(text)
air.merge(G)
archive()
SSair.add_to_active(src, 0)
@@ -1,465 +0,0 @@
/turf
//used for temperature calculations
var/thermal_conductivity = 0.05
var/heat_capacity = 1
var/temperature_archived
//list of open turfs adjacent to us
var/list/atmos_adjacent_turfs
//bitfield of dirs in which we are superconducitng
var/atmos_supeconductivity = NONE
//used to determine whether we should archive
var/archived_cycle = 0
var/current_cycle = 0
//used for mapping and for breathing while in walls (because that's a thing that needs to be accounted for...)
//string parsed by /datum/gas/proc/copy_from_turf
var/initial_gas_mix = OPENTURF_DEFAULT_ATMOS
//approximation of MOLES_O2STANDARD and MOLES_N2STANDARD pending byond allowing constant expressions to be embedded in constant strings
// If someone will place 0 of some gas there, SHIT WILL BREAK. Do not do that.
/turf/open
//used for spacewind
var/pressure_difference = 0
var/pressure_direction = 0
var/datum/excited_group/excited_group
var/excited = FALSE
var/datum/gas_mixture/turf/air
var/obj/effect/hotspot/active_hotspot
var/atmos_cooldown = 0
var/planetary_atmos = FALSE //air will revert to initial_gas_mix over time
var/list/atmos_overlay_types //gas IDs of current active gas overlays
/turf/open/Initialize()
if(!blocks_air)
air = new
air.copy_from_turf(src)
. = ..()
/turf/open/Destroy()
if(active_hotspot)
QDEL_NULL(active_hotspot)
// Adds the adjacent turfs to the current atmos processing
for(var/T in atmos_adjacent_turfs)
SSair.add_to_active(T)
return ..()
/////////////////GAS MIXTURE PROCS///////////////////
/turf/open/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
if(!giver)
return FALSE
air.merge(giver)
update_visuals()
return TRUE
/turf/open/remove_air(amount)
var/datum/gas_mixture/ours = return_air()
var/datum/gas_mixture/removed = ours.remove(amount)
update_visuals()
return removed
/turf/open/proc/copy_air_with_tile(turf/open/T)
if(istype(T))
air.copy_from(T.air)
/turf/open/proc/copy_air(datum/gas_mixture/copy)
if(copy)
air.copy_from(copy)
/turf/return_air()
var/datum/gas_mixture/GM = new
GM.copy_from_turf(src)
return GM
/turf/open/return_air()
return air
/turf/temperature_expose()
if(temperature > heat_capacity)
to_be_destroyed = TRUE
/turf/proc/archive()
temperature_archived = temperature
/turf/open/archive()
ARCHIVE_TEMPERATURE(air)
archived_cycle = SSair.times_fired
temperature_archived = temperature
/////////////////////////GAS OVERLAYS//////////////////////////////
/turf/open/proc/update_visuals()
var/list/new_overlay_types = tile_graphic()
var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
vis_contents -= overlay
if (length(new_overlay_types))
if (atmos_overlay_types)
vis_contents += new_overlay_types - atmos_overlay_types //don't add overlays that already exist
else
vis_contents += new_overlay_types
UNSETEMPTY(new_overlay_types)
src.atmos_overlay_types = new_overlay_types
/turf/open/proc/tile_graphic()
var/static/list/nonoverlaying_gases = typecache_of_gases_with_no_overlays()
if(!air)
return
. = new /list
var/list/gases = air.gases
for(var/id in gases)
if (nonoverlaying_gases[id])
continue
var/gas = gases[id]
var/gas_overlay = GLOB.meta_gas_overlays[id]
if(gas_overlay && gas > GLOB.meta_gas_visibility[id])
. += gas_overlay[min(FACTOR_GAS_VISIBLE_MAX, CEILING(gas / MOLES_GAS_VISIBLE_STEP, 1))]
/proc/typecache_of_gases_with_no_overlays()
. = list()
for (var/gastype in subtypesof(/datum/gas))
var/datum/gas/gasvar = gastype
if (!initial(gasvar.gas_overlay))
.[gastype] = TRUE
/////////////////////////////SIMULATION///////////////////////////////////
#define LAST_SHARE_CHECK \
var/last_share = our_air.last_share;\
if(last_share > MINIMUM_AIR_TO_SUSPEND){\
our_excited_group.reset_cooldowns();\
cached_atmos_cooldown = 0;\
} else if(last_share > MINIMUM_MOLES_DELTA_TO_MOVE) {\
our_excited_group.dismantle_cooldown = 0;\
cached_atmos_cooldown = 0;\
}
/turf/proc/process_cell(fire_count)
SSair.remove_from_active(src)
/turf/open/process_cell(fire_count)
if(archived_cycle < fire_count) //archive self if not already done
archive()
current_cycle = fire_count
//cache for sanic speed
var/list/adjacent_turfs = atmos_adjacent_turfs
var/datum/excited_group/our_excited_group = excited_group
var/adjacent_turfs_length = LAZYLEN(adjacent_turfs)
var/cached_atmos_cooldown = atmos_cooldown + 1
var/planet_atmos = planetary_atmos
if (planet_atmos)
adjacent_turfs_length++
var/datum/gas_mixture/our_air = air
for(var/t in adjacent_turfs)
var/turf/open/enemy_tile = t
if(fire_count <= enemy_tile.current_cycle)
continue
enemy_tile.archive()
/******************* GROUP HANDLING START *****************************************************************/
var/should_share_air = FALSE
var/datum/gas_mixture/enemy_air = enemy_tile.air
//cache for sanic speed
var/datum/excited_group/enemy_excited_group = enemy_tile.excited_group
if(our_excited_group && enemy_excited_group)
if(our_excited_group != enemy_excited_group)
//combine groups (this also handles updating the excited_group var of all involved turfs)
our_excited_group.merge_groups(enemy_excited_group)
our_excited_group = excited_group //update our cache
should_share_air = TRUE
else if(our_air.compare(enemy_air))
if(!enemy_tile.excited)
SSair.add_to_active(enemy_tile)
var/datum/excited_group/EG = our_excited_group || enemy_excited_group || new
if(!our_excited_group)
EG.add_turf(src)
if(!enemy_excited_group)
EG.add_turf(enemy_tile)
our_excited_group = excited_group
should_share_air = TRUE
//air sharing
if(should_share_air)
var/difference = our_air.share(enemy_air, adjacent_turfs_length)
if(difference)
if(difference > 0)
consider_pressure_difference(enemy_tile, difference)
else
enemy_tile.consider_pressure_difference(src, -difference)
LAST_SHARE_CHECK
/******************* GROUP HANDLING FINISH *********************************************************************/
if (planet_atmos) //share our air with the "atmosphere" "above" the turf
var/datum/gas_mixture/G = new
G.copy_from_turf(src)
ARCHIVE_TEMPERATURE(G)
if(our_air.compare(G))
if(!our_excited_group)
var/datum/excited_group/EG = new
EG.add_turf(src)
our_excited_group = excited_group
our_air.share(G, adjacent_turfs_length)
LAST_SHARE_CHECK
SSair.add_to_react_queue(src)
if((!our_excited_group && !(our_air.temperature > MINIMUM_TEMPERATURE_START_SUPERCONDUCTION && consider_superconductivity(starting = TRUE))) \
|| (cached_atmos_cooldown > (EXCITED_GROUP_DISMANTLE_CYCLES * 2)))
SSair.remove_from_active(src)
atmos_cooldown = cached_atmos_cooldown
/turf/open/space/process_cell(fire_count) //dumb hack to prevent space pollution
. = ..()
var/datum/gas_mixture/immutable/I = space_gas
I.after_process_cell()
/turf/proc/process_cell_reaction()
SSair.remove_from_react_queue(src)
/turf/open/process_cell_reaction()
air.react(src)
update_visuals()
SSair.remove_from_react_queue(src)
return
//////////////////////////SPACEWIND/////////////////////////////
/turf/open/proc/consider_pressure_difference(turf/T, difference)
SSair.high_pressure_delta |= src
if(difference > pressure_difference)
pressure_direction = get_dir(src, T)
pressure_difference = difference
/turf/open/proc/high_pressure_movements()
var/atom/movable/M
for(var/thing in src)
M = thing
if (!M.anchored && !M.pulledby && M.last_high_pressure_movement_air_cycle < SSair.times_fired)
M.experience_pressure_difference(pressure_difference, pressure_direction)
/atom/movable/var/pressure_resistance = 10
/atom/movable/var/last_high_pressure_movement_air_cycle = 0
/atom/movable/proc/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)
var/const/PROBABILITY_OFFSET = 25
var/const/PROBABILITY_BASE_PRECENT = 75
set waitfor = 0
var/move_prob = 100
if (pressure_resistance > 0)
move_prob = (pressure_difference/pressure_resistance*PROBABILITY_BASE_PRECENT)-PROBABILITY_OFFSET
move_prob += pressure_resistance_prob_delta
if (move_prob > PROBABILITY_OFFSET && prob(move_prob))
step(src, direction)
last_high_pressure_movement_air_cycle = SSair.times_fired
///////////////////////////EXCITED GROUPS/////////////////////////////
/datum/excited_group
var/list/turf_list = list()
var/breakdown_cooldown = 0
var/dismantle_cooldown = 0
/datum/excited_group/New()
SSair.excited_groups += src
/datum/excited_group/proc/add_turf(turf/open/T)
turf_list += T
T.excited_group = src
reset_cooldowns()
/datum/excited_group/proc/merge_groups(datum/excited_group/E)
if(turf_list.len > E.turf_list.len)
SSair.excited_groups -= E
for(var/t in E.turf_list)
var/turf/open/T = t
T.excited_group = src
turf_list += T
reset_cooldowns()
else
SSair.excited_groups -= src
for(var/t in turf_list)
var/turf/open/T = t
T.excited_group = E
E.turf_list += T
E.reset_cooldowns()
/datum/excited_group/proc/reset_cooldowns()
breakdown_cooldown = 0
dismantle_cooldown = 0
//argument is so world start can clear out any turf differences quickly.
/datum/excited_group/proc/self_breakdown(space_is_all_consuming = FALSE)
var/datum/gas_mixture/A = new
//make local for sanic speed
var/list/A_gases = A.gases
var/list/turf_list = src.turf_list
var/turflen = turf_list.len
var/space_in_group = FALSE
for(var/t in turf_list)
var/turf/open/T = t
if (space_is_all_consuming && !space_in_group && istype(T.air, /datum/gas_mixture/immutable/space))
space_in_group = TRUE
qdel(A)
A = new /datum/gas_mixture/immutable/space()
A_gases = A.gases //update the cache
break
A.merge(T.air)
for(var/id in A_gases)
A_gases[id] /= turflen
for(var/t in turf_list)
var/turf/open/T = t
T.air.copy_from(A)
T.atmos_cooldown = 0
T.update_visuals()
breakdown_cooldown = 0
/datum/excited_group/proc/dismantle()
for(var/t in turf_list)
var/turf/open/T = t
T.excited = FALSE
T.excited_group = null
SSair.active_turfs -= T
garbage_collect()
/datum/excited_group/proc/garbage_collect()
for(var/t in turf_list)
var/turf/open/T = t
T.excited_group = null
turf_list.Cut()
SSair.excited_groups -= src
////////////////////////SUPERCONDUCTIVITY/////////////////////////////
/turf/proc/conductivity_directions()
if(archived_cycle < SSair.times_fired)
archive()
return NORTH|SOUTH|EAST|WEST
/turf/open/conductivity_directions()
if(blocks_air)
return ..()
for(var/direction in GLOB.cardinals)
var/turf/T = get_step(src, direction)
if(!(T in atmos_adjacent_turfs) && !(atmos_supeconductivity & direction))
. |= direction
/turf/proc/neighbor_conduct_with_src(turf/open/other)
if(!other.blocks_air) //Open but neighbor is solid
other.temperature_share_open_to_solid(src)
else //Both tiles are solid
other.share_temperature_mutual_solid(src, thermal_conductivity)
temperature_expose(null, temperature, null)
/turf/open/neighbor_conduct_with_src(turf/other)
if(blocks_air)
..()
return
if(!other.blocks_air) //Both tiles are open
var/turf/open/T = other
T.air.temperature_share(air, WINDOW_HEAT_TRANSFER_COEFFICIENT)
else //Solid but neighbor is open
temperature_share_open_to_solid(other)
SSair.add_to_active(src, 0)
/turf/proc/super_conduct()
var/conductivity_directions = conductivity_directions()
if(conductivity_directions)
//Conduct with tiles around me
for(var/direction in GLOB.cardinals)
if(conductivity_directions & direction)
var/turf/neighbor = get_step(src,direction)
if(!neighbor.thermal_conductivity)
continue
if(neighbor.archived_cycle < SSair.times_fired)
neighbor.archive()
neighbor.neighbor_conduct_with_src(src)
neighbor.consider_superconductivity()
radiate_to_spess()
finish_superconduction()
/turf/proc/finish_superconduction(temp = temperature)
//Make sure still hot enough to continue conducting heat
if(temp < MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION)
SSair.active_super_conductivity -= src
return FALSE
/turf/open/finish_superconduction()
//Conduct with air on my tile if I have it
if(!blocks_air)
temperature = air.temperature_share(null, thermal_conductivity, temperature, heat_capacity)
..((blocks_air ? temperature : air.temperature))
/turf/proc/consider_superconductivity()
if(!thermal_conductivity)
return FALSE
SSair.active_super_conductivity |= src
return TRUE
/turf/open/consider_superconductivity(starting)
if(air.temperature < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION))
return FALSE
if(air.heat_capacity() < M_CELL_WITH_RATIO) // Was: MOLES_CELLSTANDARD*0.1*0.05 Since there are no variables here we can make this a constant.
return FALSE
return ..()
/turf/closed/consider_superconductivity(starting)
if(temperature < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION))
return FALSE
return ..()
/turf/proc/radiate_to_spess() //Radiate excess tile heat to space
if(temperature > T0C) //Considering 0 degC as te break even point for radiation in and out
var/delta_temperature = (temperature_archived - TCMB) //hardcoded space temperature
if((heat_capacity > 0) && (abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER))
var/heat = thermal_conductivity*delta_temperature* \
(heat_capacity*HEAT_CAPACITY_VACUUM/(heat_capacity+HEAT_CAPACITY_VACUUM))
temperature -= heat/heat_capacity
/turf/open/proc/temperature_share_open_to_solid(turf/sharer)
sharer.temperature = air.temperature_share(null, sharer.thermal_conductivity, sharer.temperature, sharer.heat_capacity)
/turf/proc/share_temperature_mutual_solid(turf/sharer, conduction_coefficient) //to be understood
var/delta_temperature = (temperature_archived - sharer.temperature_archived)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER && heat_capacity && sharer.heat_capacity)
var/heat = conduction_coefficient*delta_temperature* \
(heat_capacity*sharer.heat_capacity/(heat_capacity+sharer.heat_capacity))
temperature -= heat/heat_capacity
sharer.temperature += heat/sharer.heat_capacity
@@ -1,454 +0,0 @@
//All defines used in reactions are located in ..\__DEFINES\reactions.dm
/proc/init_gas_reactions()
. = list()
for(var/type in subtypesof(/datum/gas))
.[type] = list()
for(var/r in subtypesof(/datum/gas_reaction))
var/datum/gas_reaction/reaction = r
if(initial(reaction.exclude))
continue
reaction = new r
var/datum/gas/reaction_key
for (var/req in reaction.min_requirements)
if (ispath(req))
var/datum/gas/req_gas = req
if (!reaction_key || initial(reaction_key.rarity) > initial(req_gas.rarity))
reaction_key = req_gas
.[reaction_key] += list(reaction)
sortTim(., /proc/cmp_gas_reactions, TRUE)
/proc/cmp_gas_reactions(list/datum/gas_reaction/a, list/datum/gas_reaction/b) // compares lists of reactions by the maximum priority contained within the list
if (!length(a) || !length(b))
return length(b) - length(a)
var/maxa
var/maxb
for (var/datum/gas_reaction/R in a)
if (R.priority > maxa)
maxa = R.priority
for (var/datum/gas_reaction/R in b)
if (R.priority > maxb)
maxb = R.priority
return maxb - maxa
/datum/gas_reaction
//regarding the requirements lists: the minimum or maximum requirements must be non-zero.
//when in doubt, use MINIMUM_MOLE_COUNT.
var/list/min_requirements
var/list/max_requirements
var/exclude = FALSE //do it this way to allow for addition/removal of reactions midmatch in the future
var/priority = 100 //lower numbers are checked/react later than higher numbers. if two reactions have the same priority they may happen in either order
var/name = "reaction"
var/id = "r"
/datum/gas_reaction/New()
init_reqs()
/datum/gas_reaction/proc/init_reqs()
/datum/gas_reaction/proc/react(datum/gas_mixture/air, atom/location)
return NO_REACTION
/datum/gas_reaction/nobliumsupression
priority = INFINITY
name = "Hyper-Noblium Reaction Suppression"
id = "nobstop"
/datum/gas_reaction/nobliumsupression/init_reqs()
min_requirements = list(/datum/gas/hypernoblium = REACTION_OPPRESSION_THRESHOLD)
/datum/gas_reaction/nobliumsupression/react()
return STOP_REACTIONS
//water vapor: puts out fires?
/datum/gas_reaction/water_vapor
priority = 1
name = "Water Vapor"
id = "vapor"
/datum/gas_reaction/water_vapor/init_reqs()
min_requirements = list(/datum/gas/water_vapor = MOLES_GAS_VISIBLE)
/datum/gas_reaction/water_vapor/react(datum/gas_mixture/air, datum/holder)
var/turf/open/location = isturf(holder) ? holder : null
. = NO_REACTION
if (air.temperature <= WATER_VAPOR_FREEZE)
if(location && location.freon_gas_act())
. = REACTING
else if(location && location.water_vapor_gas_act())
air.gases[/datum/gas/water_vapor] -= MOLES_GAS_VISIBLE
. = REACTING
//tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/tritfire
priority = -1 //fire should ALWAYS be last, but tritium fires happen before plasma fires
name = "Tritium Combustion"
id = "tritfire"
/datum/gas_reaction/tritfire/init_reqs()
min_requirements = list(
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST,
/datum/gas/tritium = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT
)
/datum/gas_reaction/tritfire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
var/old_heat_capacity = air.heat_capacity()
var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
var/temperature = air.temperature
var/list/cached_results = air.reaction_results
cached_results["fire"] = 0
var/turf/open/location = isturf(holder) ? holder : null
var/burned_fuel = 0
if(cached_gases[/datum/gas/oxygen] < cached_gases[/datum/gas/tritium])
burned_fuel = cached_gases[/datum/gas/oxygen]/TRITIUM_BURN_OXY_FACTOR
cached_gases[/datum/gas/tritium] -= burned_fuel
else
burned_fuel = cached_gases[/datum/gas/tritium]*TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/tritium] -= cached_gases[/datum/gas/tritium]/TRITIUM_BURN_TRIT_FACTOR
cached_gases[/datum/gas/oxygen] -= cached_gases[/datum/gas/tritium]
if(burned_fuel)
energy_released += (FIRE_HYDROGEN_ENERGY_RELEASED * burned_fuel)
if(location && prob(10) && burned_fuel > TRITIUM_MINIMUM_RADIATION_ENERGY) //woah there let's not crash the server
radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR)
cached_gases[/datum/gas/water_vapor] += burned_fuel/TRITIUM_BURN_OXY_FACTOR
cached_results["fire"] += burned_fuel
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
//let the floor know a fire is happening
if(istype(location))
temperature = air.temperature
if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
location.hotspot_expose(temperature, CELL_VOLUME)
for(var/I in location)
var/atom/movable/item = I
item.temperature_expose(air, temperature, CELL_VOLUME)
location.temperature_expose(air, temperature, CELL_VOLUME)
return cached_results["fire"] ? REACTING : NO_REACTION
//plasma combustion: combustion of oxygen and plasma (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/plasmafire
priority = -2 //fire should ALWAYS be last, but plasma fires happen after tritium fires
name = "Plasma Combustion"
id = "plasmafire"
/datum/gas_reaction/plasmafire/init_reqs()
min_requirements = list(
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST,
/datum/gas/plasma = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT
)
/datum/gas_reaction/plasmafire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
var/old_heat_capacity = air.heat_capacity()
var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
var/temperature = air.temperature
var/list/cached_results = air.reaction_results
cached_results["fire"] = 0
var/turf/open/location = isturf(holder) ? holder : null
//Handle plasma burning
var/plasma_burn_rate = 0
var/oxygen_burn_rate = 0
//more plasma released at higher temperatures
var/temperature_scale = 0
//to make tritium
var/super_saturation = FALSE
if(temperature > PLASMA_UPPER_TEMPERATURE)
temperature_scale = 1
else
temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
if(temperature_scale > 0)
oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale
if(cached_gases[/datum/gas/oxygen] / cached_gases[/datum/gas/plasma] > SUPER_SATURATION_THRESHOLD) //supersaturation. Form Tritium.
super_saturation = TRUE
if(cached_gases[/datum/gas/oxygen] > cached_gases[/datum/gas/plasma]*PLASMA_OXYGEN_FULLBURN)
plasma_burn_rate = (cached_gases[/datum/gas/plasma]*temperature_scale)/PLASMA_BURN_RATE_DELTA
else
plasma_burn_rate = (temperature_scale*(cached_gases[/datum/gas/oxygen]/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
plasma_burn_rate = min(plasma_burn_rate,cached_gases[/datum/gas/plasma],cached_gases[/datum/gas/oxygen]/oxygen_burn_rate) //Ensures matter is conserved properly
cached_gases[/datum/gas/plasma] = QUANTIZE(cached_gases[/datum/gas/plasma] - plasma_burn_rate)
cached_gases[/datum/gas/oxygen] = QUANTIZE(cached_gases[/datum/gas/oxygen] - (plasma_burn_rate * oxygen_burn_rate))
if (super_saturation)
cached_gases[/datum/gas/tritium] += plasma_burn_rate
else
cached_gases[/datum/gas/carbon_dioxide] += plasma_burn_rate
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
cached_results["fire"] += (plasma_burn_rate)*(1+oxygen_burn_rate)
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
//let the floor know a fire is happening
if(istype(location))
temperature = air.temperature
if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
location.hotspot_expose(temperature, CELL_VOLUME)
for(var/I in location)
var/atom/movable/item = I
item.temperature_expose(air, temperature, CELL_VOLUME)
location.temperature_expose(air, temperature, CELL_VOLUME)
return cached_results["fire"] ? REACTING : NO_REACTION
//fusion: a terrible idea that was fun but broken. Now reworked to be less broken and more interesting. Again (and again, and again). Again!
//Fusion Rework Counter: Please increment this if you make a major overhaul to this system again.
//6 reworks
/datum/gas_reaction/fusion
exclude = FALSE
priority = 2
name = "Plasmic Fusion"
id = "fusion"
/datum/gas_reaction/fusion/init_reqs()
min_requirements = list(
"TEMP" = FUSION_TEMPERATURE_THRESHOLD,
/datum/gas/tritium = FUSION_TRITIUM_MOLES_USED,
/datum/gas/plasma = FUSION_MOLE_THRESHOLD,
/datum/gas/carbon_dioxide = FUSION_MOLE_THRESHOLD)
/datum/gas_reaction/fusion/react(datum/gas_mixture/air, datum/holder)
var/list/cached_gases = air.gases
var/turf/open/location
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))
else
location = get_turf(holder)
if(!air.analyzer_results)
air.analyzer_results = new
var/list/cached_scan_results = air.analyzer_results
var/old_heat_capacity = air.heat_capacity()
var/reaction_energy = 0 //Reaction energy can be negative or positive, for both exothermic and endothermic reactions.
var/initial_plasma = cached_gases[/datum/gas/plasma]
var/initial_carbon = cached_gases[/datum/gas/carbon_dioxide]
var/scale_factor = (air.volume)/(PI) //We scale it down by volume/Pi because for fusion conditions, moles roughly = 2*volume, but we want it to be based off something constant between reactions.
var/toroidal_size = (2*PI)+TORADIANS(arctan((air.volume-TOROID_VOLUME_BREAKEVEN)/TOROID_VOLUME_BREAKEVEN)) //The size of the phase space hypertorus
var/gas_power = 0
var/list/gas_fusion_powers = GLOB.meta_gas_fusions
for (var/gas_id in cached_gases)
gas_power += (gas_fusion_powers[gas_id]*cached_gases[gas_id])
var/instability = MODULUS((gas_power*INSTABILITY_GAS_POWER_FACTOR)**2,toroidal_size) //Instability effects how chaotic the behavior of the reaction is
cached_scan_results[id] = instability//used for analyzer feedback
var/plasma = (initial_plasma-FUSION_MOLE_THRESHOLD)/(scale_factor) //We have to scale the amounts of carbon and plasma down a significant amount in order to show the chaotic dynamics we want
var/carbon = (initial_carbon-FUSION_MOLE_THRESHOLD)/(scale_factor) //We also subtract out the threshold amount to make it harder for fusion to burn itself out.
//The reaction is a specific form of the Kicked Rotator system, which displays chaotic behavior and can be used to model particle interactions.
plasma = MODULUS(plasma - (instability*sin(TODEGREES(carbon))), toroidal_size)
carbon = MODULUS(carbon - plasma, toroidal_size)
cached_gases[/datum/gas/plasma] = plasma*scale_factor + FUSION_MOLE_THRESHOLD //Scales the gases back up
cached_gases[/datum/gas/carbon_dioxide] = carbon*scale_factor + FUSION_MOLE_THRESHOLD
var/delta_plasma = initial_plasma - cached_gases[/datum/gas/plasma]
reaction_energy += delta_plasma*PLASMA_BINDING_ENERGY //Energy is gained or lost corresponding to the creation or destruction of mass.
if(instability < FUSION_INSTABILITY_ENDOTHERMALITY)
reaction_energy = max(reaction_energy,0) //Stable reactions don't end up endothermic.
else if (reaction_energy < 0)
reaction_energy *= (instability-FUSION_INSTABILITY_ENDOTHERMALITY)**0.5
if(air.thermal_energy() + reaction_energy < 0) //No using energy that doesn't exist.
cached_gases[/datum/gas/plasma] = initial_plasma
cached_gases[/datum/gas/carbon_dioxide] = initial_carbon
return NO_REACTION
cached_gases[/datum/gas/tritium] -= FUSION_TRITIUM_MOLES_USED
//The decay of the tritium and the reaction's energy produces waste gases, different ones depending on whether the reaction is endo or exothermic
if(reaction_energy > 0)
cached_gases[/datum/gas/oxygen] += FUSION_TRITIUM_MOLES_USED*(reaction_energy*FUSION_TRITIUM_CONVERSION_COEFFICIENT)
cached_gases[/datum/gas/nitrous_oxide] += FUSION_TRITIUM_MOLES_USED*(reaction_energy*FUSION_TRITIUM_CONVERSION_COEFFICIENT)
else
cached_gases[/datum/gas/bz] += FUSION_TRITIUM_MOLES_USED*(reaction_energy*-FUSION_TRITIUM_CONVERSION_COEFFICIENT)
cached_gases[/datum/gas/nitryl] += FUSION_TRITIUM_MOLES_USED*(reaction_energy*-FUSION_TRITIUM_CONVERSION_COEFFICIENT)
if(reaction_energy)
if(location)
var/particle_chance = ((PARTICLE_CHANCE_CONSTANT)/(reaction_energy-PARTICLE_CHANCE_CONSTANT)) + 1//Asymptopically approaches 100% as the energy of the reaction goes up.
if(prob(PERCENT(particle_chance)))
location.fire_nuclear_particle()
var/rad_power = max((FUSION_RAD_COEFFICIENT/instability) + FUSION_RAD_MAX,0)
radiation_pulse(location,rad_power)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = CLAMP(((air.temperature*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB,INFINITY)
return REACTING
/datum/gas_reaction/nitrylformation //The formation of nitryl. Endothermic. Requires N2O as a catalyst.
priority = 3
name = "Nitryl formation"
id = "nitrylformation"
/datum/gas_reaction/nitrylformation/init_reqs()
min_requirements = list(
/datum/gas/oxygen = 20,
/datum/gas/nitrogen = 20,
/datum/gas/nitrous_oxide = 5,
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST*400
)
/datum/gas_reaction/nitrylformation/react(datum/gas_mixture/air)
var/list/cached_gases = air.gases
var/temperature = air.temperature
var/old_heat_capacity = air.heat_capacity()
var/heat_efficency = min(temperature/(FIRE_MINIMUM_TEMPERATURE_TO_EXIST*100),cached_gases[/datum/gas/oxygen],cached_gases[/datum/gas/nitrogen])
var/energy_used = heat_efficency*NITRYL_FORMATION_ENERGY
if ((cached_gases[/datum/gas/oxygen] - heat_efficency < 0 )|| (cached_gases[/datum/gas/nitrogen] - heat_efficency < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
cached_gases[/datum/gas/oxygen] -= heat_efficency
cached_gases[/datum/gas/nitrogen] -= heat_efficency
cached_gases[/datum/gas/nitryl] += heat_efficency*2
if(energy_used > 0)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature*old_heat_capacity - energy_used)/new_heat_capacity),TCMB)
return REACTING
/datum/gas_reaction/bzformation //Formation of BZ by combining plasma and tritium at low pressures. Exothermic.
priority = 4
name = "BZ Gas formation"
id = "bzformation"
/datum/gas_reaction/bzformation/init_reqs()
min_requirements = list(
/datum/gas/nitrous_oxide = 10,
/datum/gas/plasma = 10
)
/datum/gas_reaction/bzformation/react(datum/gas_mixture/air)
var/list/cached_gases = air.gases
var/temperature = air.temperature
var/pressure = air.return_pressure()
var/old_heat_capacity = air.heat_capacity()
var/reaction_efficency = min(1/((pressure/(0.1*ONE_ATMOSPHERE))*(max(cached_gases[/datum/gas/plasma]/cached_gases[/datum/gas/nitrous_oxide],1))),cached_gases[/datum/gas/nitrous_oxide],cached_gases[/datum/gas/plasma]/2)
var/energy_released = 2*reaction_efficency*FIRE_CARBON_ENERGY_RELEASED
if ((cached_gases[/datum/gas/nitrous_oxide] - reaction_efficency < 0 )|| (cached_gases[/datum/gas/plasma] - (2*reaction_efficency) < 0) || energy_released <= 0) //Shouldn't produce gas from nothing.
return NO_REACTION
cached_gases[/datum/gas/bz] += reaction_efficency
if(reaction_efficency == cached_gases[/datum/gas/nitrous_oxide])
cached_gases[/datum/gas/bz] -= min(pressure,1)
cached_gases[/datum/gas/oxygen] += min(pressure,1)
cached_gases[/datum/gas/nitrous_oxide] -= reaction_efficency
cached_gases[/datum/gas/plasma] -= 2*reaction_efficency
SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, min((reaction_efficency**2)*BZ_RESEARCH_SCALE),BZ_RESEARCH_MAX_AMOUNT)
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature*old_heat_capacity + energy_released)/new_heat_capacity),TCMB)
return REACTING
/datum/gas_reaction/stimformation //Stimulum formation follows a strange pattern of how effective it will be at a given temperature, having some multiple peaks and some large dropoffs. Exo and endo thermic.
priority = 5
name = "Stimulum formation"
id = "stimformation"
/datum/gas_reaction/stimformation/init_reqs()
min_requirements = list(
/datum/gas/tritium = 30,
/datum/gas/plasma = 10,
/datum/gas/bz = 20,
/datum/gas/nitryl = 30,
"TEMP" = STIMULUM_HEAT_SCALE/2)
/datum/gas_reaction/stimformation/react(datum/gas_mixture/air)
var/list/cached_gases = air.gases
var/old_heat_capacity = air.heat_capacity()
var/heat_scale = min(air.temperature/STIMULUM_HEAT_SCALE,cached_gases[/datum/gas/tritium],cached_gases[/datum/gas/plasma],cached_gases[/datum/gas/nitryl])
var/stim_energy_change = heat_scale + STIMULUM_FIRST_RISE*(heat_scale**2) - STIMULUM_FIRST_DROP*(heat_scale**3) + STIMULUM_SECOND_RISE*(heat_scale**4) - STIMULUM_ABSOLUTE_DROP*(heat_scale**5)
if ((cached_gases[/datum/gas/tritium] - heat_scale < 0 )|| (cached_gases[/datum/gas/plasma] - heat_scale < 0) || (cached_gases[/datum/gas/nitryl] - heat_scale < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
cached_gases[/datum/gas/stimulum]+= heat_scale/10
cached_gases[/datum/gas/tritium] -= heat_scale
cached_gases[/datum/gas/plasma] -= heat_scale
cached_gases[/datum/gas/nitryl] -= heat_scale
SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, STIMULUM_RESEARCH_AMOUNT*max(stim_energy_change,0))
if(stim_energy_change)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((air.temperature*old_heat_capacity + stim_energy_change)/new_heat_capacity),TCMB)
return REACTING
/datum/gas_reaction/nobliumformation //Hyper-Noblium formation is extrememly endothermic, but requires high temperatures to start. Due to its high mass, hyper-nobelium uses large amounts of nitrogen and tritium. BZ can be used as a catalyst to make it less endothermic.
priority = 6
name = "Hyper-Noblium condensation"
id = "nobformation"
/datum/gas_reaction/nobliumformation/init_reqs()
min_requirements = list(
/datum/gas/nitrogen = 10,
/datum/gas/tritium = 5,
"TEMP" = 5000000)
/datum/gas_reaction/nobliumformation/react(datum/gas_mixture/air)
var/list/cached_gases = air.gases
var/old_heat_capacity = air.heat_capacity()
var/nob_formed = min((cached_gases[/datum/gas/nitrogen]+cached_gases[/datum/gas/tritium])/100,cached_gases[/datum/gas/tritium]/10,cached_gases[/datum/gas/nitrogen]/20)
var/energy_taken = nob_formed*(NOBLIUM_FORMATION_ENERGY/(max(cached_gases[/datum/gas/bz],1)))
if ((cached_gases[/datum/gas/tritium] - 10*nob_formed < 0) || (cached_gases[/datum/gas/nitrogen] - 20*nob_formed < 0))
return NO_REACTION
cached_gases[/datum/gas/tritium] -= 10*nob_formed
cached_gases[/datum/gas/nitrogen] -= 20*nob_formed
cached_gases[/datum/gas/hypernoblium]+= nob_formed
SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, nob_formed*NOBLIUM_RESEARCH_AMOUNT)
if (nob_formed)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((air.temperature*old_heat_capacity - energy_taken)/new_heat_capacity),TCMB)
/datum/gas_reaction/miaster //dry heat sterilization: clears out pathogens in the air
priority = -10 //after all the heating from fires etc. is done
name = "Dry Heat Sterilization"
id = "sterilization"
/datum/gas_reaction/miaster/init_reqs()
min_requirements = list(
"TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST+70,
/datum/gas/miasma = MINIMUM_MOLE_COUNT
)
/datum/gas_reaction/miaster/react(datum/gas_mixture/air, datum/holder)
var/list/cached_gases = air.gases
// As the name says it, it needs to be dry
if(cached_gases[/datum/gas/water_vapor] && cached_gases[/datum/gas/water_vapor]/air.total_moles() > 0.1)
return
//Replace miasma with oxygen
var/cleaned_air = min(cached_gases[/datum/gas/miasma], 20 + (air.temperature - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20)
cached_gases[/datum/gas/miasma] -= cleaned_air
cached_gases[/datum/gas/oxygen] += cleaned_air
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
air.temperature += cleaned_air * 0.002
SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, cleaned_air*MIASMA_RESEARCH_AMOUNT)//Turns out the burning of miasma is kinda interesting to scientists
@@ -1,358 +0,0 @@
/*
Quick overview:
Pipes combine to form pipelines
Pipelines and other atmospheric objects combine to form pipe_networks
Note: A single pipe_network represents a completely open space
Pipes -> Pipelines
Pipelines + Other Objects -> Pipe network
*/
#define PIPE_VISIBLE_LEVEL 2
#define PIPE_HIDDEN_LEVEL 1
/obj/machinery/atmospherics
anchored = TRUE
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
layer = GAS_PIPE_HIDDEN_LAYER //under wires
resistance_flags = FIRE_PROOF
max_integrity = 200
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
var/nodealert = 0
var/can_unwrench = 0
var/initialize_directions = 0
var/pipe_color
var/piping_layer = PIPING_LAYER_DEFAULT
var/pipe_flags = NONE
var/static/list/iconsetids = list()
var/static/list/pipeimages = list()
var/image/pipe_vision_img = null
var/device_type = 0
var/list/obj/machinery/atmospherics/nodes
var/construction_type
var/pipe_state //icon_state as a pipe item
var/on = FALSE
/obj/machinery/atmospherics/examine(mob/user)
..()
if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user))
var/mob/living/L = user
if(L.ventcrawler)
to_chat(L, "<span class='notice'>Alt-click to crawl through it.</span>")
/obj/machinery/atmospherics/New(loc, process = TRUE, setdir)
if(!isnull(setdir))
setDir(setdir)
if(pipe_flags & PIPING_CARDINAL_AUTONORMALIZE)
normalize_cardinal_directions()
nodes = new(device_type)
if (!armor)
armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
..()
if(process)
SSair.atmos_machinery += src
SetInitDirections()
/obj/machinery/atmospherics/Destroy()
for(var/i in 1 to device_type)
nullifyNode(i)
SSair.atmos_machinery -= src
dropContents()
if(pipe_vision_img)
qdel(pipe_vision_img)
return ..()
//return QDEL_HINT_FINDREFERENCE
/obj/machinery/atmospherics/proc/destroy_network()
return
/obj/machinery/atmospherics/proc/build_network()
// Called to build a network from this node
return
/obj/machinery/atmospherics/proc/nullifyNode(i)
if(nodes[i])
var/obj/machinery/atmospherics/N = nodes[i]
N.disconnect(src)
nodes[i] = null
/obj/machinery/atmospherics/proc/getNodeConnects()
var/list/node_connects = list()
node_connects.len = device_type
for(var/i in 1 to device_type)
for(var/D in GLOB.cardinals)
if(D & GetInitDirections())
if(D in node_connects)
continue
node_connects[i] = D
break
return node_connects
/obj/machinery/atmospherics/proc/normalize_cardinal_directions()
if(dir==SOUTH)
setDir(NORTH)
else if(dir==WEST)
setDir(EAST)
//this is called just after the air controller sets up turfs
/obj/machinery/atmospherics/proc/atmosinit(var/list/node_connects)
if(!node_connects) //for pipes where order of nodes doesn't matter
node_connects = getNodeConnects()
for(var/i in 1 to device_type)
for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i]))
if(can_be_node(target, i))
nodes[i] = target
break
update_icon()
/obj/machinery/atmospherics/proc/setPipingLayer(new_layer)
if(pipe_flags & PIPING_DEFAULT_LAYER_ONLY)
new_layer = PIPING_LAYER_DEFAULT
piping_layer = new_layer
pixel_x = (piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_X
pixel_y = (piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y
layer = initial(layer) + ((piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_LCHANGE)
/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration)
return connection_check(target, piping_layer)
//Find a connecting /obj/machinery/atmospherics in specified direction
/obj/machinery/atmospherics/proc/findConnecting(direction, prompted_layer)
for(var/obj/machinery/atmospherics/target in get_step(src, direction))
if(target.initialize_directions & get_dir(target,src))
if(connection_check(target, prompted_layer))
return target
/obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer)
if(isConnectable(target, given_layer) && target.isConnectable(src, given_layer) && (target.initialize_directions & get_dir(target,src)))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/isConnectable(obj/machinery/atmospherics/target, given_layer)
if(isnull(given_layer))
given_layer = piping_layer
if((target.piping_layer == given_layer) || (target.pipe_flags & PIPING_ALL_LAYER))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/pipeline_expansion()
return nodes
/obj/machinery/atmospherics/proc/SetInitDirections()
return
/obj/machinery/atmospherics/proc/GetInitDirections()
return initialize_directions
/obj/machinery/atmospherics/proc/returnPipenet()
return
/obj/machinery/atmospherics/proc/returnPipenetAir()
return
/obj/machinery/atmospherics/proc/setPipenet()
return
/obj/machinery/atmospherics/proc/replacePipenet()
return
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
if(istype(reference, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = reference
P.destroy_network()
nodes[nodes.Find(reference)] = null
update_icon()
/obj/machinery/atmospherics/update_icon()
return
/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/pipe)) //lets you autodrop
var/obj/item/pipe/pipe = W
if(user.dropItemToGround(pipe))
pipe.setPipingLayer(piping_layer) //align it with us
return TRUE
else
return ..()
/obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I)
if(!can_unwrench(user))
return ..()
var/turf/T = get_turf(src)
if (level==1 && isturf(T) && T.intact)
to_chat(user, "<span class='warning'>You must remove the plating first!</span>")
return TRUE
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
add_fingerprint(user)
var/unsafe_wrenching = FALSE
var/internal_pressure = int_air.return_pressure()-env_air.return_pressure()
to_chat(user, "<span class='notice'>You begin to unfasten \the [src]...</span>")
if (internal_pressure > 2*ONE_ATMOSPHERE)
to_chat(user, "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>")
unsafe_wrenching = TRUE //Oh dear oh dear
if(I.use_tool(src, user, 20, volume=50))
user.visible_message( \
"[user] unfastens \the [src].", \
"<span class='notice'>You unfasten \the [src].</span>", \
"<span class='italics'>You hear ratchet.</span>")
investigate_log("was <span class='warning'>REMOVED</span> by [key_name(usr)]", INVESTIGATE_ATMOS)
//You unwrenched a pipe full of pressure? Let's splat you into the wall, silly.
if(unsafe_wrenching)
unsafe_pressure_release(user, internal_pressure)
deconstruct(TRUE)
return TRUE
/obj/machinery/atmospherics/proc/can_unwrench(mob/user)
return can_unwrench
// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure.
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
return
if(!pressures)
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
pressures = int_air.return_pressure() - env_air.return_pressure()
var/fuck_you_dir = get_dir(src, user) // Because fuck you...
if(!fuck_you_dir)
fuck_you_dir = pick(GLOB.cardinals)
var/turf/target = get_edge_target_turf(user, fuck_you_dir)
var/range = pressures/250
var/speed = range/5
user.visible_message("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
user.throw_at(target, range, speed)
/obj/machinery/atmospherics/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(can_unwrench)
var/obj/item/pipe/stored = new construction_type(loc, null, dir, src)
stored.setPipingLayer(piping_layer)
if(!disassembled)
stored.obj_integrity = stored.max_integrity * 0.5
transfer_fingerprints_to(stored)
..()
/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255))
//Add identifiers for the iconset
if(iconsetids[iconset] == null)
iconsetids[iconset] = num2text(iconsetids.len + 1)
//Generate a unique identifier for this image combination
var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]"
if((!(. = pipeimages[identifier])))
var/image/pipe_overlay
pipe_overlay = . = pipeimages[identifier] = image(iconset, iconstate, dir = direction)
pipe_overlay.color = col
/obj/machinery/atmospherics/proc/icon_addintact(var/obj/machinery/atmospherics/node)
var/image/img = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "pipe_intact", get_dir(src,node), node.pipe_color)
underlays += img
return img.dir
/obj/machinery/atmospherics/proc/icon_addbroken(var/connected = FALSE)
var/unconnected = (~connected) & initialize_directions
for(var/direction in GLOB.cardinals)
if(unconnected & direction)
underlays += getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "pipe_exposed", direction)
/obj/machinery/atmospherics/on_construction(obj_color, set_layer)
if(can_unwrench)
add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY)
pipe_color = obj_color
setPipingLayer(set_layer)
var/turf/T = get_turf(src)
level = T.intact ? 2 : 1
atmosinit()
var/list/nodes = pipeline_expansion()
for(var/obj/machinery/atmospherics/A in nodes)
A.atmosinit()
A.addMember(src)
build_network()
/obj/machinery/atmospherics/Entered(atom/movable/AM)
if(istype(AM, /mob/living))
var/mob/living/L = AM
L.ventcrawl_layer = piping_layer
return ..()
/obj/machinery/atmospherics/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
deconstruct(FALSE)
return ..()
#define VENT_SOUND_DELAY 30
/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
direction &= initialize_directions
if(!direction || !(direction in GLOB.cardinals)) //cant go this way.
return
if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
return
var/obj/machinery/atmospherics/target_move = findConnecting(direction, user.ventcrawl_layer)
if(target_move)
if(target_move.can_crawl_through())
if(is_type_in_typecache(target_move, GLOB.ventcrawl_machinery))
user.forceMove(target_move.loc) //handle entering and so on.
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>","<span class='notice'>You climb out the ventilation system.")
else
var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets()
if(pipenetdiff.len)
user.update_pipe_vision(target_move)
user.forceMove(target_move)
user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement
if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
user.last_played_vent = world.time
playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
else if(is_type_in_typecache(src, GLOB.ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
user.forceMove(loc)
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>","<span class='notice'>You climb out the ventilation system.")
user.canmove = FALSE
addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1)
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_list(src, GLOB.ventcrawl_machinery))
L.handle_ventcrawl(src)
return
..()
/obj/machinery/atmospherics/proc/can_crawl_through()
return TRUE
/obj/machinery/atmospherics/proc/returnPipenets()
return list()
/obj/machinery/atmospherics/update_remote_sight(mob/user)
user.sight |= (SEE_TURFS|BLIND)
//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it.
/obj/machinery/atmospherics/proc/can_see_pipes()
return TRUE
@@ -1,445 +0,0 @@
/obj/machinery/atmospherics/components/unary/cryo_cell
name = "cryo cell"
icon = 'icons/obj/cryogenics.dmi'
icon_state = "pod-off"
density = TRUE
max_integrity = 350
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30)
layer = ABOVE_WINDOW_LAYER
state_open = FALSE
circuit = /obj/item/circuitboard/machine/cryo_tube
pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
occupant_typecache = list(/mob/living/carbon, /mob/living/simple_animal)
var/autoeject = FALSE
var/volume = 100
var/efficiency = 1
var/sleep_factor = 0.00125
var/unconscious_factor = 0.001
var/heat_capacity = 20000
var/conduction_coefficient = 0.3
var/obj/item/reagent_containers/glass/beaker = null
var/reagent_transfer = 0
var/obj/item/radio/radio
var/radio_key = /obj/item/encryptionkey/headset_med
var/radio_channel = RADIO_CHANNEL_MEDICAL
var/running_anim = FALSE
var/escape_in_progress = FALSE
var/message_cooldown
var/breakout_time = 300
/obj/machinery/atmospherics/components/unary/cryo_cell/Initialize()
. = ..()
initialize_directions = dir
radio = new(src)
radio.keyslot = new radio_key
radio.subspace_transmission = TRUE
radio.canhear_range = 0
radio.recalculateChannels()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_construction()
..(dir, dir)
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
var/C
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
C += M.rating
efficiency = initial(efficiency) * C
sleep_factor = initial(sleep_factor) * C
unconscious_factor = initial(unconscious_factor) * C
heat_capacity = initial(heat_capacity) / C
conduction_coefficient = initial(conduction_coefficient) * C
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
QDEL_NULL(radio)
QDEL_NULL(beaker)
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target)
..()
if(beaker)
beaker.ex_act(severity, target)
/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A)
..()
if(A == beaker)
beaker = null
updateUsrDialog()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_deconstruction()
if(beaker)
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
cut_overlays()
if(panel_open)
add_overlay("pod-panel")
if(state_open)
icon_state = "pod-open"
return
if(occupant)
var/image/occupant_overlay
if(ismonkey(occupant)) // Monkey
occupant_overlay = image(CRYOMOBS, "monkey")
else if(isalienadult(occupant))
if(isalienroyal(occupant)) // Queen and prae
occupant_overlay = image(CRYOMOBS, "alienq")
else if(isalienhunter(occupant)) // Hunter
occupant_overlay = image(CRYOMOBS, "alienh")
else if(isaliensentinel(occupant)) // Sentinel
occupant_overlay = image(CRYOMOBS, "aliens")
else // Drone or other
occupant_overlay = image(CRYOMOBS, "aliend")
else if(ishuman(occupant) || islarva(occupant) || (isanimal(occupant) && !ismegafauna(occupant))) // Mobs that are smaller than cryotube
occupant_overlay = image(occupant.icon, occupant.icon_state)
occupant_overlay.copy_overlays(occupant)
else
occupant_overlay = image(CRYOMOBS, "generic")
occupant_overlay.dir = SOUTH
occupant_overlay.pixel_y = 22
if(on && !running_anim && is_operational())
icon_state = "pod-on"
running_anim = TRUE
run_anim(TRUE, occupant_overlay)
else
icon_state = "pod-off"
add_overlay(occupant_overlay)
add_overlay("cover-off")
else if(on && is_operational())
icon_state = "pod-on"
add_overlay("cover-on")
else
icon_state = "pod-off"
add_overlay("cover-off")
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/run_anim(anim_up, image/occupant_overlay)
if(!on || !occupant || !is_operational())
running_anim = FALSE
return
cut_overlays()
if(occupant_overlay.pixel_y != 23) // Same effect as occupant_overlay.pixel_y == 22 || occupant_overlay.pixel_y == 24
anim_up = occupant_overlay.pixel_y == 22 // Same effect as if(occupant_overlay.pixel_y == 22) anim_up = TRUE ; if(occupant_overlay.pixel_y == 24) anim_up = FALSE
if(anim_up)
occupant_overlay.pixel_y++
else
occupant_overlay.pixel_y--
add_overlay(occupant_overlay)
add_overlay("cover-on")
addtimer(CALLBACK(src, .proc/run_anim, anim_up, occupant_overlay), 7, TIMER_UNIQUE)
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
..()
if(!on)
return
if(!is_operational())
on = FALSE
update_icon()
return
if(!occupant)
return
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat == DEAD) // We don't bother with dead people.
return
if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people.
on = FALSE
update_icon()
playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
var/msg = "Patient fully restored."
if(autoeject) // Eject if configured.
msg += " Auto ejecting patient now."
open_machine()
radio.talk_into(src, msg, radio_channel)
return
var/datum/gas_mixture/air1 = airs[1]
if(air1.gases.len)
if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic.
mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000)
mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000)
if(beaker)
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents.
beaker.reagents.reaction(occupant, VAPOR)
air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this
GAS_GARBAGE_COLLECT(air1.gases)
if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker).
reagent_transfer = 0
return 1
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
..()
if(!on)
return
var/datum/gas_mixture/air1 = airs[1]
if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work.
on = FALSE
update_icon()
return
if(occupant)
var/mob/living/mob_occupant = occupant
var/cold_protection = 0
var/temperature_delta = air1.temperature - mob_occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant.
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
cold_protection = H.get_thermal_protection(air1.temperature, TRUE)
if(abs(temperature_delta) > 1)
var/air_heat_capacity = air1.heat_capacity()
var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity))
air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB)
mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB)
air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic.
GAS_GARBAGE_COLLECT(air1.gases)
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
if(message_cooldown <= world.time)
message_cooldown = world.time + 50
to_chat(user, "<span class='warning'>[src]'s door won't budge!</span>")
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0)
if(!state_open && !panel_open)
on = FALSE
..()
for(var/mob/M in contents) //only drop mobs
M.forceMove(get_turf(src))
if(isliving(M))
var/mob/living/L = M
L.update_canmove()
occupant = null
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the glass of [src]!</span>", \
"<span class='notice'>You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a thump from [src].</span>")
if(do_after(user, breakout_time, target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src )
return
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
..()
if(occupant)
if(on)
to_chat(user, "Someone's inside [src]!")
else
to_chat(user, "You can barely make out a form floating in [src].")
else
to_chat(user, "[src] seems empty.")
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious())
close_machine(target)
else
user.visible_message("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
if (do_after(user, 25, target=target))
close_machine(target)
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
. = 1 //no afterattack
if(beaker)
to_chat(user, "<span class='warning'>A beaker is already loaded into [src]!</span>")
return
if(!user.transferItemToLoc(I, src))
return
beaker = I
user.visible_message("[user] places [I] in [src].", \
"<span class='notice'>You place [I] in [src].</span>")
var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list)
log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]")
return
if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \
|| default_change_direction_wrench(user, I) \
|| default_pry_open(I) \
|| default_deconstruction_crowbar(I))
update_icon()
return
else if(istype(I, /obj/item/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
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
var/list/data = list()
data["isOperating"] = on
data["hasOccupant"] = occupant ? TRUE : FALSE
data["isOpen"] = state_open
data["autoEject"] = autoeject
data["occupant"] = list()
if(occupant)
var/mob/living/mob_occupant = occupant
data["occupant"]["name"] = mob_occupant.name
switch(mob_occupant.stat)
if(CONSCIOUS)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "good"
if(SOFT_CRIT)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "average"
if(UNCONSCIOUS)
data["occupant"]["stat"] = "Unconscious"
data["occupant"]["statstate"] = "average"
if(DEAD)
data["occupant"]["stat"] = "Dead"
data["occupant"]["statstate"] = "bad"
data["occupant"]["health"] = round(mob_occupant.health, 1)
data["occupant"]["maxHealth"] = mob_occupant.maxHealth
data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
data["occupant"]["bruteLoss"] = round(mob_occupant.getBruteLoss(), 1)
data["occupant"]["oxyLoss"] = round(mob_occupant.getOxyLoss(), 1)
data["occupant"]["toxLoss"] = round(mob_occupant.getToxLoss(), 1)
data["occupant"]["fireLoss"] = round(mob_occupant.getFireLoss(), 1)
data["occupant"]["bodyTemperature"] = round(mob_occupant.bodytemperature, 1)
if(mob_occupant.bodytemperature < TCRYO)
data["occupant"]["temperaturestatus"] = "good"
else if(mob_occupant.bodytemperature < T0C)
data["occupant"]["temperaturestatus"] = "average"
else
data["occupant"]["temperaturestatus"] = "bad"
var/datum/gas_mixture/air1 = airs[1]
data["cellTemperature"] = round(air1.temperature, 1)
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
var/beakerContents = list()
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents += list(list("name" = R.name, "volume" = R.volume))
data["beakerContents"] = beakerContents
return data
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
if(..())
return
switch(action)
if("power")
if(on)
on = FALSE
else if(!state_open)
on = TRUE
. = TRUE
if("door")
if(state_open)
close_machine()
else
open_machine()
. = TRUE
if("autoeject")
autoeject = !autoeject
. = TRUE
if("ejectbeaker")
if(beaker)
beaker.forceMove(drop_location())
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
. = TRUE
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open)
on = !on
update_icon()
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(state_open)
close_machine()
else
open_machine()
update_icon()
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user)
return // we don't see the pipe network while inside cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user)
user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through()
return // can't ventcrawl in or out of cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes()
return 0 // you can't see the pipe network when inside a cryo cell.
/obj/machinery/atmospherics/components/unary/cryo_cell/return_temperature()
var/datum/gas_mixture/G = airs[1]
if(G.total_moles() > 10)
return G.temperature
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W)
. = ..()
if(.)
SetInitDirections()
var/obj/machinery/atmospherics/node = nodes[1]
if(node)
node.disconnect(src)
nodes[1] = null
nullifyPipenet(parents[1])
atmosinit()
node = nodes[1]
if(node)
node.atmosinit()
node.addMember(src)
build_network()
#undef CRYOMOBS
@@ -487,7 +487,7 @@
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf
/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly=FALSE)
/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
if(visualsOnly)
return
var/list/no_drops = list()
@@ -529,7 +529,7 @@
r_hand = /obj/item/gun/energy/laser/instakill/blue
shoes = /obj/item/clothing/shoes/jackboots/fast
/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H)
/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
..()
var/obj/item/radio/R = H.ears
R.set_frequency(FREQ_CTF_RED)
@@ -537,7 +537,7 @@
R.independent = TRUE
H.dna.species.stunmod = 0
/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H)
/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
..()
var/obj/item/radio/R = H.ears
R.set_frequency(FREQ_CTF_BLUE)
-650
View File
@@ -1,650 +0,0 @@
//If someone can do this in a neater way, be my guest-Kor
//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now).
/obj/effect/mob_spawn
name = "Unknown"
density = TRUE
anchored = TRUE
var/job_description = null
var/mob_type = null
var/mob_name = ""
var/mob_gender = null
var/death = TRUE //Kill the mob
var/roundstart = TRUE //fires on initialize
var/instant = FALSE //fires on New
var/flavour_text = "The mapper forgot to set this!"
var/faction = null
var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses.
var/random = FALSE //Don't set a name or gender, just go random
var/antagonist_type
var/objectives = null
var/uses = 1 //how many times can we spawn from it. set to -1 for infinite.
var/brute_damage = 0
var/oxy_damage = 0
var/burn_damage = 0
var/datum/disease/disease = null //Do they start with a pre-spawned disease?
var/mob_color //Change the mob's color
var/assignedrole
var/show_flavour = TRUE
var/banType = "lavaland"
var/ghost_usable = TRUE
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/attack_ghost(mob/user, latejoinercalling)
if(!SSticker.HasRoundStarted() || !loc || !ghost_usable)
return
if(!uses)
to_chat(user, "<span class='warning'>This spawner is out of charges!</span>")
return
if(jobban_isbanned(user, banType))
to_chat(user, "<span class='warning'>You are jobanned!</span>")
return
if(QDELETED(src) || QDELETED(user))
return
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round())
return FALSE
var/ghost_role = alert(latejoinercalling ? "Latejoin as [mob_name]? (This is a ghost role, and as such, it's very likely to be off-station.)" : "Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No")
if(ghost_role == "No" || !loc)
return
if(QDELETED(src) || QDELETED(user))
return
if(latejoinercalling)
var/mob/dead/new_player/NP = user
if(istype(NP))
NP.close_spawn_windows()
NP.stop_sound_channel(CHANNEL_LOBBYMUSIC)
log_game("[key_name(user)] became [mob_name]")
create(ckey = user.ckey)
return TRUE
/obj/effect/mob_spawn/Initialize(mapload)
. = ..()
if(instant || (roundstart && (mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP))))
create()
else if(ghost_usable)
GLOB.poi_list |= src
LAZYADD(GLOB.mob_spawners[job_description ? job_description : name], src)
/obj/effect/mob_spawn/Destroy()
GLOB.poi_list -= src
var/job_name = job_description ? job_description : name
LAZYREMOVE(GLOB.mob_spawners[job_name], src)
if(!LAZYLEN(GLOB.mob_spawners[job_name]))
GLOB.mob_spawners -= job_name
return ..()
/obj/effect/mob_spawn/proc/can_latejoin() //If it can be taken from the lobby.
return ghost_usable
/obj/effect/mob_spawn/proc/special(mob/M)
return
/obj/effect/mob_spawn/proc/equip(mob/M)
return
/obj/effect/mob_spawn/proc/create(ckey, name)
var/mob/living/M = new mob_type(get_turf(src)) //living mobs only
if(!random)
M.real_name = mob_name ? mob_name : M.name
if(!mob_gender)
mob_gender = pick(MALE, FEMALE)
M.gender = mob_gender
if(faction)
M.faction = list(faction)
if(disease)
M.ForceContractDisease(new disease)
if(death)
M.death(1) //Kills the new mob
M.adjustOxyLoss(oxy_damage)
M.adjustBruteLoss(brute_damage)
M.adjustFireLoss(burn_damage)
M.color = mob_color
equip(M)
if(ckey)
M.ckey = ckey
if(show_flavour)
to_chat(M, "[flavour_text]")
var/datum/mind/MM = M.mind
var/datum/antagonist/A
if(antagonist_type)
A = MM.add_antag_datum(antagonist_type)
if(objectives)
if(!A)
A = MM.add_antag_datum(/datum/antagonist/custom)
for(var/objective in objectives)
var/datum/objective/O = new/datum/objective(objective)
O.owner = MM
A.objectives += O
if(assignedrole)
M.mind.assigned_role = assignedrole
special(M, name)
MM.name = M.real_name
if(uses > 0)
uses--
if(!permanent && !uses)
qdel(src)
// Base version - place these on maps/templates.
/obj/effect/mob_spawn/human
mob_type = /mob/living/carbon/human
//Human specific stuff.
var/mob_species = null //Set to make them a mutant race such as lizard or skeleton. Uses the datum typepath instead of the ID.
var/datum/outfit/outfit = /datum/outfit //If this is a path, it will be instanced in Initialize()
var/disable_pda = TRUE
var/disable_sensors = TRUE
//All of these only affect the ID that the outfit has placed in the ID slot
var/id_job = null //Such as "Clown" or "Chef." This just determines what the ID reads as, not their access
var/id_access = null //This is for access. See access.dm for which jobs give what access. Use "Captain" if you want it to be all access.
var/id_access_list = null //Allows you to manually add access to an ID card.
assignedrole = "Ghost Role"
var/husk = null
//these vars are for lazy mappers to override parts of the outfit
//these cannot be null by default, or mappers cannot set them to null if they want nothing in that slot
var/uniform = -1
var/r_hand = -1
var/l_hand = -1
var/suit = -1
var/shoes = -1
var/gloves = -1
var/ears = -1
var/glasses = -1
var/mask = -1
var/head = -1
var/belt = -1
var/r_pocket = -1
var/l_pocket = -1
var/back = -1
var/id = -1
var/neck = -1
var/backpack_contents = -1
var/suit_store = -1
var/hair_style
var/facial_hair_style
var/skin_tone
/obj/effect/mob_spawn/human/Initialize()
if(ispath(outfit))
outfit = new outfit()
if(!outfit)
outfit = new /datum/outfit
return ..()
/obj/effect/mob_spawn/human/equip(mob/living/carbon/human/H)
if(mob_species)
H.set_species(mob_species)
if(husk)
H.Drain()
else //Because for some reason I can't track down, things are getting turned into husks even if husk = false. It's in some damage proc somewhere.
H.cure_husk()
H.underwear = "Nude"
H.undershirt = "Nude"
H.socks = "Nude"
if(hair_style)
H.hair_style = hair_style
else
H.hair_style = random_hair_style(gender)
if(facial_hair_style)
H.facial_hair_style = facial_hair_style
else
H.facial_hair_style = random_facial_hair_style(gender)
if(skin_tone)
H.skin_tone = skin_tone
else
H.skin_tone = random_skin_tone()
H.update_hair()
H.update_body()
if(outfit)
var/static/list/slots = list("uniform", "r_hand", "l_hand", "suit", "shoes", "gloves", "ears", "glasses", "mask", "head", "belt", "r_pocket", "l_pocket", "back", "id", "neck", "backpack_contents", "suit_store")
for(var/slot in slots)
var/T = vars[slot]
if(!isnum(T))
outfit.vars[slot] = T
H.equipOutfit(outfit)
if(disable_pda)
// We don't want corpse PDAs to show up in the messenger list.
var/obj/item/pda/PDA = locate(/obj/item/pda) in H
if(PDA)
PDA.toff = TRUE
if(disable_sensors)
// Using crew monitors to find corpses while creative makes finding certain ruins too easy.
var/obj/item/clothing/under/C = H.w_uniform
if(istype(C))
C.sensor_mode = NO_SENSORS
var/obj/item/card/id/W = H.wear_id
if(W)
if(id_access)
for(var/jobtype in typesof(/datum/job))
var/datum/job/J = new jobtype
if(J.title == id_access)
W.access = J.get_access()
break
if(id_access_list)
if(!islist(W.access))
W.access = list()
W.access |= id_access_list
if(id_job)
W.assignment = id_job
W.registered_name = H.real_name
W.update_label()
//Instant version - use when spawning corpses during runtime
/obj/effect/mob_spawn/human/corpse
roundstart = FALSE
instant = TRUE
/obj/effect/mob_spawn/human/corpse/damaged
brute_damage = 1000
/obj/effect/mob_spawn/human/alive
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
death = FALSE
roundstart = FALSE //you could use these for alive fake humans on roundstart but this is more common scenario
/obj/effect/mob_spawn/human/corpse/delayed
ghost_usable = FALSE //These are just not-yet-set corpses.
instant = FALSE
//Non-human spawners
/obj/effect/mob_spawn/AICorpse/create(ckey, name) //Creates a corrupted AI
var/A = locate(/mob/living/silicon/ai) in loc
if(A)
return
var/mob/living/silicon/ai/spawned/M = new(loc) //spawn new AI at landmark as var M
M.name = src.name
M.real_name = src.name
M.aiPDA.toff = TRUE //turns the AI's PDA messenger off, stopping it showing up on player PDAs
M.death() //call the AI's death proc
qdel(src)
/obj/effect/mob_spawn/slime
mob_type = /mob/living/simple_animal/slime
var/mobcolour = "grey"
icon = 'icons/mob/slimes.dmi'
icon_state = "grey baby slime" //sets the icon in the map editor
/obj/effect/mob_spawn/slime/equip(mob/living/simple_animal/slime/S)
S.colour = mobcolour
/obj/effect/mob_spawn/human/facehugger/create(ckey, name) //Creates a squashed facehugger
var/obj/item/clothing/mask/facehugger/O = new(src.loc) //variable O is a new facehugger at the location of the landmark
O.name = src.name
O.Die() //call the facehugger's death proc
qdel(src)
/obj/effect/mob_spawn/mouse
name = "sleeper"
mob_type = /mob/living/simple_animal/mouse
death = FALSE
roundstart = FALSE
job_description = "Mouse"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
/obj/effect/mob_spawn/cow
name = "sleeper"
mob_type = /mob/living/simple_animal/cow
death = FALSE
roundstart = FALSE
job_description = "Cow"
mob_gender = FEMALE
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example.
///////////Civilians//////////////////////
/obj/effect/mob_spawn/human/corpse/assistant
name = "Assistant"
outfit = /datum/outfit/job/assistant
/obj/effect/mob_spawn/human/corpse/assistant/beesease_infection
disease = /datum/disease/beesease
/obj/effect/mob_spawn/human/corpse/assistant/brainrot_infection
disease = /datum/disease/brainrot
/obj/effect/mob_spawn/human/corpse/assistant/spanishflu_infection
disease = /datum/disease/fluspanish
/obj/effect/mob_spawn/human/corpse/cargo_tech
name = "Cargo Tech"
outfit = /datum/outfit/job/cargo_tech
/obj/effect/mob_spawn/human/cook
name = "Cook"
outfit = /datum/outfit/job/cook
/obj/effect/mob_spawn/human/doctor
name = "Doctor"
outfit = /datum/outfit/job/doctor
/obj/effect/mob_spawn/human/doctor/alive
death = FALSE
roundstart = FALSE
random = TRUE
name = "sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
flavour_text = "<span class='big bold'>You are a space doctor!</span>"
assignedrole = "Space Doctor"
job_description = "Off-station Doctor"
/obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H)
..()
// Remove radio and PDA so they wouldn't annoy station crew.
var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset)
for(var/del_type in del_types)
var/obj/item/I = locate(del_type) in H
qdel(I)
/obj/effect/mob_spawn/human/engineer
name = "Engineer"
outfit = /datum/outfit/job/engineer/gloved
/obj/effect/mob_spawn/human/engineer/rig
outfit = /datum/outfit/job/engineer/gloved/rig
/obj/effect/mob_spawn/human/clown
name = "Clown"
outfit = /datum/outfit/job/clown
/obj/effect/mob_spawn/human/scientist
name = "Scientist"
outfit = /datum/outfit/job/scientist
/obj/effect/mob_spawn/human/miner
name = "Shaft Miner"
outfit = /datum/outfit/job/miner
/obj/effect/mob_spawn/human/miner/rig
outfit = /datum/outfit/job/miner/equipped/hardsuit
/obj/effect/mob_spawn/human/miner/explorer
outfit = /datum/outfit/job/miner/equipped
/obj/effect/mob_spawn/human/plasmaman
mob_species = /datum/species/plasmaman
outfit = /datum/outfit/plasmaman
/obj/effect/mob_spawn/human/bartender
name = "Space Bartender"
id_job = "Bartender"
id_access_list = list(ACCESS_BAR)
outfit = /datum/outfit/spacebartender
/obj/effect/mob_spawn/human/bartender/alive
death = FALSE
roundstart = FALSE
random = TRUE
job_description = "Off-station Bartender"
name = "bartender sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
flavour_text = "<span class='big bold'>You are a space bartender!</span><b> Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect.</b>"
assignedrole = "Space Bartender"
id_job = "Bartender"
/datum/outfit/spacebartender
name = "Space Bartender"
uniform = /obj/item/clothing/under/rank/bartender
back = /obj/item/storage/backpack
shoes = /obj/item/clothing/shoes/sneakers/black
suit = /obj/item/clothing/suit/armor/vest
glasses = /obj/item/clothing/glasses/sunglasses/reagent
id = /obj/item/card/id
/obj/effect/mob_spawn/human/beach
outfit = /datum/outfit/beachbum
/obj/effect/mob_spawn/human/beach/alive
death = FALSE
roundstart = FALSE
random = TRUE
job_description = "Beach Biodome Bum"
mob_name = "Beach Bum"
name = "beach bum sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
flavour_text = "<span class='big bold'>You're, like, totally a dudebro, bruh.</span><b> Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?</b>"
assignedrole = "Beach Bum"
/obj/effect/mob_spawn/human/beach/alive/lifeguard
flavour_text = "<span class='big bold'>You're a spunky lifeguard!</span><b> It's up to you to make sure nobody drowns or gets eaten by sharks and stuff.</b>"
mob_gender = "female"
name = "lifeguard sleeper"
id_job = "Lifeguard"
job_description = "Beach Biodome Lifeguard"
uniform = /obj/item/clothing/under/shorts/red
/datum/outfit/beachbum
name = "Beach Bum"
glasses = /obj/item/clothing/glasses/sunglasses
r_pocket = /obj/item/storage/wallet/random
l_pocket = /obj/item/reagent_containers/food/snacks/pizzaslice/dank;
uniform = /obj/item/clothing/under/pants/youngfolksjeans
id = /obj/item/card/id
/datum/outfit/beachbum/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
return
H.dna.add_mutation(STONER)
/////////////////Officers+Nanotrasen Security//////////////////////
/obj/effect/mob_spawn/human/bridgeofficer
name = "Bridge Officer"
id_job = "Bridge Officer"
id_access_list = list(ACCESS_CENT_CAPTAIN)
outfit = /datum/outfit/nanotrasenbridgeofficercorpse
/datum/outfit/nanotrasenbridgeofficercorpse
name = "Bridge Officer Corpse"
ears = /obj/item/radio/headset/heads/hop
uniform = /obj/item/clothing/under/rank/centcom_officer
suit = /obj/item/clothing/suit/armor/bulletproof
shoes = /obj/item/clothing/shoes/sneakers/black
glasses = /obj/item/clothing/glasses/sunglasses
id = /obj/item/card/id
/obj/effect/mob_spawn/human/commander
name = "Commander"
id_job = "Commander"
id_access_list = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE)
outfit = /datum/outfit/nanotrasencommandercorpse
/datum/outfit/nanotrasencommandercorpse
name = "Nanotrasen Private Security Commander"
uniform = /obj/item/clothing/under/rank/centcom_commander
suit = /obj/item/clothing/suit/armor/bulletproof
ears = /obj/item/radio/headset/heads/captain
glasses = /obj/item/clothing/glasses/eyepatch
mask = /obj/item/clothing/mask/cigarette/cigar/cohiba
head = /obj/item/clothing/head/centhat
gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/combat/swat
r_pocket = /obj/item/lighter
id = /obj/item/card/id
/obj/effect/mob_spawn/human/nanotrasensoldier
name = "Nanotrasen Private Security Officer"
id_job = "Private Security Force"
id_access_list = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_SECURITY)
outfit = /datum/outfit/nanotrasensoldiercorpse
/datum/outfit/nanotrasensoldiercorpse
name = "NT Private Security Officer Corpse"
uniform = /obj/item/clothing/under/rank/security
suit = /obj/item/clothing/suit/armor/vest
shoes = /obj/item/clothing/shoes/combat
gloves = /obj/item/clothing/gloves/combat
mask = /obj/item/clothing/mask/gas/sechailer/swat
head = /obj/item/clothing/head/helmet/swat/nanotrasen
back = /obj/item/storage/backpack/security
id = /obj/item/card/id
/obj/effect/mob_spawn/human/commander/alive
death = FALSE
roundstart = FALSE
job_description = "Nanotrasen Commander"
mob_name = "Nanotrasen Commander"
name = "sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
flavour_text = "<span class='big bold'>You are a Nanotrasen Commander!</span>"
/obj/effect/mob_spawn/human/nanotrasensoldier/alive
death = FALSE
roundstart = FALSE
mob_name = "Private Security Officer"
job_description = "Nanotrasen Security"
name = "sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
faction = "nanotrasenprivate"
flavour_text = "<span class='big bold'>You are a Nanotrasen Private Security Officer!</span>"
/////////////////Spooky Undead//////////////////////
/obj/effect/mob_spawn/human/skeleton
name = "skeletal remains"
mob_name = "skeleton"
mob_species = /datum/species/skeleton
mob_gender = NEUTER
/obj/effect/mob_spawn/human/skeleton/alive
death = FALSE
roundstart = FALSE
job_description = "Skeleton"
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
flavour_text = "<span class='big bold'>By unknown powers, your skeletal remains have been reanimated!</span><b> Walk this mortal plain and terrorize all living adventurers who dare cross your path.</b>"
assignedrole = "Skeleton"
/obj/effect/mob_spawn/human/zombie
name = "rotting corpse"
mob_name = "zombie"
mob_species = /datum/species/zombie
assignedrole = "Zombie"
/obj/effect/mob_spawn/human/zombie/alive
death = FALSE
roundstart = FALSE
job_description = "Zombie"
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
flavour_text = "<span class='big bold'>By unknown powers, your rotting remains have been resurrected!</span><b> Walk this mortal plain and terrorize all living adventurers who dare cross your path.</b>"
/obj/effect/mob_spawn/human/abductor
name = "abductor"
mob_name = "alien"
mob_species = /datum/species/abductor
outfit = /datum/outfit/abductorcorpse
/datum/outfit/abductorcorpse
name = "Abductor Corpse"
uniform = /obj/item/clothing/under/color/grey
shoes = /obj/item/clothing/shoes/combat
//For ghost bar.
/obj/effect/mob_spawn/human/alive/space_bar_patron
name = "Bar cryogenics"
mob_name = "Bar patron"
random = TRUE
permanent = TRUE
uses = -1
outfit = /datum/outfit/spacebartender
assignedrole = "Space Bar Patron"
job_description = "Space Bar Patron"
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user)
var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No")
if(despawn == "No" || !loc || !Adjacent(user))
return
user.visible_message("<span class='notice'>[user.name] climbs back into cryosleep...</span>")
qdel(user)
/datum/outfit/cryobartender
name = "Cryogenic Bartender"
uniform = /obj/item/clothing/under/rank/bartender
back = /obj/item/storage/backpack
shoes = /obj/item/clothing/shoes/sneakers/black
suit = /obj/item/clothing/suit/armor/vest
glasses = /obj/item/clothing/glasses/sunglasses/reagent
/obj/effect/mob_spawn/human/lavaknight
name = "odd cryogenics pod"
desc = "A humming cryo pod. You can barely recognise a faint glow underneath the built up ice. The machine is attempting to wake up its occupant."
mob_name = "a displaced knight from another dimension"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
roundstart = FALSE
id_job = "Knight"
job_description = "Cydonian Knight"
death = FALSE
random = TRUE
outfit = /datum/outfit/lavaknight
mob_species = /datum/species/human
flavour_text = "<font size=3><b>Y</b></font><b>ou are a knight who conveniently has some form of retrograde amnesia. \
You cannot remember where you came from. However, a few things remain burnt into your mind, most prominently a vow to never harm another sapient being under any circumstances unless it is hellbent on ending your life. \
Remember: hostile creatures and such are fair game for attacking, but <span class='danger'>under no circumstances are you to attack anything capable of thought and/or speech</span> unless it has made it its life's calling to chase you to the ends of the earth."
assignedrole = "Cydonian Knight"
/obj/effect/mob_spawn/human/lavaknight/special(mob/living/new_spawn)
if(ishuman(new_spawn))
var/mob/living/carbon/human/H = new_spawn
H.dna.features["mam_ears"] = "Cat, Big" //cat people
H.dna.features["mcolor"] = H.hair_color
H.update_body()
/obj/effect/mob_spawn/human/lavaknight/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
return ..()
/datum/outfit/lavaknight
name = "Cydonian Knight"
uniform = /obj/item/clothing/under/assistantformal
mask = /obj/item/clothing/mask/breath
shoes = /obj/item/clothing/shoes/sneakers/black
r_pocket = /obj/item/melee/transforming/energy/sword/cx
suit = /obj/item/clothing/suit/space/hardsuit/lavaknight
suit_store = /obj/item/tank/internals/oxygen
id = /obj/item/card/id/knight/blue
/obj/effect/mob_spawn/human/lavaknight/captain
name = "odd gilded cryogenics pod"
desc = "A humming cryo pod that appears to be gilded. You can barely recognise a faint glow underneath the built up ice. The machine is attempting to wake up its occupant."
flavour_text = "<font size=3><b>Y</b></font><b>ou are a knight who conveniently has some form of retrograde amnesia. \
You cannot remember where you came from. However, a few things remain burnt into your mind, most prominently a vow to never harm another sapient being under any circumstances unless it is hellbent on ending your life. \
Remember: hostile creatures and such are fair game for attacking, but <span class='danger'>under no circumstances are you to attack anything capable of thought and/or speech</span> unless it has made it its life's calling to chase you to the ends of the earth. \
You feel a natural instict to lead, and as such, you should strive to lead your comrades to safety, and hopefully home. You also feel a burning determination to uphold your vow, as well as your fellow comrade's."
outfit = /datum/outfit/lavaknight/captain
id_job = "Knight Captain"
/datum/outfit/lavaknight/captain
name ="Cydonian Knight Captain"
l_pocket = /obj/item/twohanded/dualsaber/hypereutactic
id = /obj/item/card/id/knight/captain
@@ -1,348 +0,0 @@
//Academy Areas
/area/awaymission/academy
name = "Academy Asteroids"
icon_state = "away"
/area/awaymission/academy/headmaster
name = "Academy Fore Block"
icon_state = "away1"
/area/awaymission/academy/classrooms
name = "Academy Classroom Block"
icon_state = "away2"
/area/awaymission/academy/academyaft
name = "Academy Ship Aft Block"
icon_state = "away3"
/area/awaymission/academy/academygate
name = "Academy Gateway"
icon_state = "away4"
/area/awaymission/academy/academycellar
name = "Academy Cellar"
icon_state = "away4"
/area/awaymission/academy/academyengine
name = "Academy Engine"
icon_state = "away4"
//Academy Items
/obj/item/paper/fluff/awaymissions/academy/console_maint
name = "Console Maintenance"
info = "We're upgrading to the latest mainframes for our consoles, the shipment should be in before spring break is over!"
/obj/item/paper/fluff/awaymissions/academy/class/automotive
name = "Automotive Repair 101"
/obj/item/paper/fluff/awaymissions/academy/class/pyromancy
name = "Pyromancy 250"
/obj/item/paper/fluff/awaymissions/academy/class/biology
name = "Biology Lab"
/obj/item/paper/fluff/awaymissions/academy/grade/aplus
name = "Summoning Midterm Exam"
info = "Grade: A+ Educator's Notes: Excellent form."
/obj/item/paper/fluff/awaymissions/academy/grade/bminus
name = "Summoning Midterm Exam"
info = "Grade: B- Educator's Notes: Keep applying yourself, you're showing improvement."
/obj/item/paper/fluff/awaymissions/academy/grade/dminus
name = "Summoning Midterm Exam"
info = "Grade: D- Educator's Notes: SEE ME AFTER CLASS."
/obj/item/paper/fluff/awaymissions/academy/grade/failure
name = "Pyromancy Evaluation"
info = "Current Grade: F. Educator's Notes: No improvement shown despite multiple private lessons. Suggest additional tutelage."
/obj/singularity/academy
dissipate = 0
move_self = 0
grav_pull = 1
/obj/singularity/academy/admin_investigate_setup()
return
/obj/singularity/academy/process()
eat()
if(prob(1))
mezzer()
/obj/item/clothing/glasses/meson/truesight
name = "The Lens of Truesight"
desc = "I can see forever!"
icon_state = "monocle"
item_state = "headset"
/obj/structure/academy_wizard_spawner
name = "Academy Defensive System"
desc = "Made by Abjuration, Inc."
icon = 'icons/obj/cult.dmi'
icon_state = "forge"
anchored = TRUE
max_integrity = 200
var/mob/living/current_wizard = null
var/next_check = 0
var/cooldown = 600
var/faction = ROLE_WIZARD
var/braindead_check = 0
/obj/structure/academy_wizard_spawner/New()
START_PROCESSING(SSobj, src)
/obj/structure/academy_wizard_spawner/Destroy()
if(!broken)
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/academy_wizard_spawner/process()
if(next_check < world.time)
if(!current_wizard)
for(var/mob/living/L in GLOB.player_list)
if(L.z == src.z && L.stat != DEAD && !(faction in L.faction))
summon_wizard()
break
else
if(current_wizard.stat == DEAD)
current_wizard = null
summon_wizard()
if(!current_wizard.client)
if(!braindead_check)
braindead_check = 1
else
braindead_check = 0
give_control()
next_check = world.time + cooldown
/obj/structure/academy_wizard_spawner/proc/give_control()
set waitfor = FALSE
if(!current_wizard)
return
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, null, ROLE_WIZARD, 50, current_wizard)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Wizard Academy Defender")
current_wizard.ghostize() // on the off chance braindead defender gets back in
C.transfer_ckey(current_wizard, FALSE)
/obj/structure/academy_wizard_spawner/proc/summon_wizard()
var/turf/T = src.loc
var/mob/living/carbon/human/wizbody = new(T)
wizbody.fully_replace_character_name(wizbody.real_name, "Academy Teacher")
wizbody.mind_initialize()
var/datum/mind/wizmind = wizbody.mind
wizmind.special_role = "Academy Defender"
wizmind.add_antag_datum(/datum/antagonist/wizard/academy)
current_wizard = wizbody
give_control()
/obj/structure/academy_wizard_spawner/deconstruct(disassembled = TRUE)
if(!broken)
broken = 1
visible_message("<span class='warning'>[src] breaks down!</span>")
icon_state = "forge_off"
STOP_PROCESSING(SSobj, src)
/datum/outfit/wizard/academy
name = "Academy Wizard"
r_pocket = null
r_hand = null
suit = /obj/item/clothing/suit/wizrobe/red
head = /obj/item/clothing/head/wizard/red
backpack_contents = list(/obj/item/storage/box/survival = 1)
/obj/item/dice/d20/fate
name = "Die of Fate"
desc = "A die with twenty sides. You can feel unearthly energies radiating from it. Using this might be VERY risky."
icon_state = "d20"
sides = 20
can_be_rigged = FALSE
var/reusable = 1
var/used = 0
/obj/item/dice/d20/fate/one_use
reusable = 0
/obj/item/dice/d20/fate/diceroll(mob/user)
..()
if(!used)
if(!ishuman(user) || !user.mind || (user.mind in SSticker.mode.wizards))
to_chat(user, "<span class='warning'>You feel the magic of the dice is restricted to ordinary humans!</span>")
return
if(rigged)
effect(user,rigged)
else
effect(user,result)
/obj/item/dice/d20/fate/equipped(mob/user, slot)
if(!ishuman(user) || !user.mind || (user.mind in SSticker.mode.wizards))
to_chat(user, "<span class='warning'>You feel the magic of the dice is restricted to ordinary humans! You should leave it alone.</span>")
user.dropItemToGround(src)
/obj/item/dice/d20/fate/proc/effect(var/mob/living/carbon/human/user,roll)
if(!reusable)
used = 1
visible_message("<span class='userdanger'>The die flare briefly.</span>")
switch(roll)
if(1)
//Dust
user.dust()
if(2)
//Death
user.death()
if(3)
//Swarm of creatures
for(var/direction in GLOB.alldirs)
var/turf/T = get_turf(src)
new /mob/living/simple_animal/hostile/netherworld(get_step(T,direction))
if(4)
//Destroy Equipment
for (var/obj/item/I in user)
qdel(I)
if(5)
//Monkeying
user.monkeyize()
if(6)
//Cut speed
var/datum/species/S = user.dna.species
S.speedmod += 1
if(7)
//Throw
user.Stun(60)
user.adjustBruteLoss(50)
var/throw_dir = pick(GLOB.cardinals)
var/atom/throw_target = get_edge_target_turf(user, throw_dir)
user.throw_at(throw_target, 200, 4)
if(8)
//Fueltank Explosion
explosion(loc,-1,0,2, flame_range = 2)
if(9)
//Cold
var/datum/disease/D = new /datum/disease/cold()
user.ForceContractDisease(D, FALSE, TRUE)
if(10)
//Nothing
visible_message("<span class='notice'>[src] roll perfectly.</span>")
if(11)
//Cookie
var/obj/item/reagent_containers/food/snacks/cookie/C = new(drop_location())
C.name = "Cookie of Fate"
if(12)
//Healing
user.revive(full_heal = 1, admin_revive = 1)
if(13)
//Mad Dosh
var/turf/Start = get_turf(src)
for(var/direction in GLOB.alldirs)
var/turf/T = get_step(Start,direction)
if(rand(0,1))
new /obj/item/stack/spacecash/c1000(T)
else
var/obj/item/storage/bag/money/M = new(T)
for(var/i in 1 to rand(5,50))
new /obj/item/coin/gold(M)
if(14)
//Free Gun
new /obj/item/gun/ballistic/revolver/mateba(drop_location())
if(15)
//Random One-use spellbook
new /obj/item/book/granter/spell/random(drop_location())
if(16)
//Servant & Servant Summon
var/mob/living/carbon/human/H = new(drop_location())
H.equipOutfit(/datum/outfit/butler)
var/datum/mind/servant_mind = new /datum/mind()
var/datum/antagonist/magic_servant/A = new
servant_mind.add_antag_datum(A)
A.setup_master(user)
servant_mind.transfer_to(H)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, null, ROLE_WIZARD, 50, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
C.transfer_ckey(H, FALSE)
var/obj/effect/proc_holder/spell/targeted/summonmob/S = new
S.target_mob = H
user.mind.AddSpell(S)
if(17)
//Tator Kit
new /obj/item/storage/box/syndicate(drop_location())
if(18)
//Captain ID
new /obj/item/card/id/captains_spare(drop_location())
if(19)
//Instrinct Resistance
to_chat(user, "<span class='notice'>You feel robust.</span>")
var/datum/species/S = user.dna.species
S.brutemod *= 0.5
S.burnmod *= 0.5
S.coldmod *= 0.5
if(20)
//Free wizard!
user.mind.make_Wizard()
/datum/outfit/butler
name = "Butler"
uniform = /obj/item/clothing/under/suit_jacket/really_black
shoes = /obj/item/clothing/shoes/laceup
head = /obj/item/clothing/head/bowler
glasses = /obj/item/clothing/glasses/monocle
gloves = /obj/item/clothing/gloves/color/white
/obj/effect/proc_holder/spell/targeted/summonmob
name = "Summon Servant"
desc = "This spell can be used to call your servant, whenever you need it."
charge_max = 100
clothes_req = 0
invocation = "JE VES"
invocation_type = "whisper"
range = -1
level_max = 0 //cannot be improved
cooldown_min = 100
include_user = 1
var/mob/living/target_mob
action_icon_state = "summons"
/obj/effect/proc_holder/spell/targeted/summonmob/cast(list/targets,mob/user = usr)
if(!target_mob)
return
var/turf/Start = get_turf(user)
for(var/direction in GLOB.alldirs)
var/turf/T = get_step(Start,direction)
if(!T.density)
target_mob.Move(T)
/obj/structure/ladder/unbreakable/rune
name = "\improper Teleportation Rune"
desc = "Could lead anywhere."
icon = 'icons/obj/rune.dmi'
icon_state = "1"
color = rgb(0,0,255)
/obj/structure/ladder/unbreakable/rune/update_icon()
return
/obj/structure/ladder/unbreakable/rune/show_fluff_message(up,mob/user)
user.visible_message("[user] activates \the [src].","<span class='notice'>You activate \the [src].</span>")
/obj/structure/ladder/unbreakable/rune/use(mob/user, is_ghost=FALSE)
if(is_ghost || !(user.mind in SSticker.mode.wizards))
..()
@@ -1,681 +0,0 @@
//Snow Valley Areas//--
/area/awaymission/snowdin
name = "Snowdin"
icon_state = "awaycontent1"
requires_power = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
/area/awaymission/snowdin/outside
name = "Snowdin Tundra Plains"
icon_state = "awaycontent25"
/area/awaymission/snowdin/outside/vip
name = "Snowdin Tundra Plains"
icon_state = "awaycontent25"
/area/awaymission/snowdin/post
name = "Snowdin Outpost"
icon_state = "awaycontent2"
requires_power = TRUE
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
/area/awaymission/snowdin/post/medbay
name = "Snowdin Outpost - Medbay"
icon_state = "awaycontent3"
/area/awaymission/snowdin/post/secpost
name = "Snowdin Outpost - Security Checkpoint"
icon_state = "awaycontent4"
/area/awaymission/snowdin/post/hydro
name = "Snowdin Outpost - Hydroponics"
icon_state = "awaycontent5"
/area/awaymission/snowdin/post/messhall
name = "Snowdin Outpost - Mess Hall"
icon_state = "awaycontent6"
/area/awaymission/snowdin/post/gateway
name = "Snowdin Outpost - Gateway"
icon_state = "awaycontent7"
/area/awaymission/snowdin/post/dorm
name = "Snowdin Outpost - Dorms"
icon_state = "awaycontent8"
/area/awaymission/snowdin/post/kitchen
name = "Snowdin Outpost - Kitchen"
icon_state = "awaycontent9"
/area/awaymission/snowdin/post/engineering
name = "Snowdin Outpost - Engineering"
icon_state = "awaycontent10"
/area/awaymission/snowdin/post/custodials
name = "Snowdin Outpost - Custodials"
icon_state = "awaycontent11"
/area/awaymission/snowdin/post/research
name = "Snowdin Outpost - Research Area"
icon_state = "awaycontent12"
/area/awaymission/snowdin/post/garage
name = "Snowdin Outpost - Garage"
icon_state = "awaycontent13"
/area/awaymission/snowdin/post/minipost
name = "Snowdin Outpost - Recon Post"
icon_state = "awaycontent19"
/area/awaymission/snowdin/post/mining_main
name = "Snowdin Outpost - Mining Post"
icon_state = "awaycontent21"
/area/awaymission/snowdin/post/mining_main/mechbay
name = "Snowdin Outpost - Mining Post Mechbay"
icon_state = "awaycontent25"
/area/awaymission/snowdin/post/mining_main/robotics
name = "Snowdin Outpost - Mining Post Robotics"
icon_state = "awaycontent26"
/area/awaymission/snowdin/post/cavern1
name = "Snowdin Outpost - Cavern Outpost 1"
icon_state = "awaycontent27"
/area/awaymission/snowdin/post/cavern2
name = "Snowdin Outpost - Cavern Outpost 2"
icon_state = "awaycontent28"
/area/awaymission/snowdin/post/mining_dock
name = "Snowdin Outpost - Underground Mine Post"
icon_state = "awaycontent22"
/area/awaymission/snowdin/post/broken_shuttle
name = "Snowdin Outpost - Broken Transit Shuttle"
icon_state = "awaycontent20"
requires_power = FALSE
/area/awaymission/snowdin/igloo
name = "Snowdin Igloos"
icon_state = "awaycontent14"
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
/area/awaymission/snowdin/cave
name = "Snowdin Caves"
icon_state = "awaycontent15"
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
/area/awaymission/snowdin/cave/cavern
name = "Snowdin Depths"
icon_state = "awaycontent23"
/area/awaymission/snowdin/cave/mountain
name = "Snowdin Mountains"
icon_state = "awaycontent24"
/area/awaymission/snowdin/base
name = "Snowdin Main Base"
icon_state = "awaycontent16"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
requires_power = FALSE
/area/awaymission/snowdin/dungeon1
name = "Snowdin Depths"
icon_state = "awaycontent17"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
/area/awaymission/snowdin/sekret
name = "Snowdin Operations"
icon_state = "awaycontent18"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
requires_power = TRUE
/area/shuttle/snowdin/elevator1
name = "Excavation Elevator"
/area/shuttle/snowdin/elevator2
name = "Mining Elevator"
//shuttle console for elevators//
/obj/machinery/computer/shuttle/snowdin/mining
name = "shuttle console"
desc = "A shuttle control computer."
icon_screen = "shuttle"
icon_keyboard = "tech_key"
light_color = LIGHT_COLOR_CYAN
shuttleId = "snowdin_mining"
possible_destinations = "snowdin_mining_top;snowdin_mining_down"
//liquid plasma!!!!!!//
/turf/open/floor/plasteel/dark/snowdin
initial_gas_mix = FROZEN_ATMOS
planetary_atmos = 1
temperature = 180
/turf/open/lava/plasma
name = "liquid plasma"
desc = "A flowing stream of chilled liquid plasma. You probably shouldn't get in."
icon_state = "liquidplasma"
initial_gas_mix = "o2=0;n2=82;plasma=24;TEMP=120"
baseturfs = /turf/open/lava/plasma
slowdown = 2
light_range = 3
light_power = 0.75
light_color = LIGHT_COLOR_PURPLE
/turf/open/lava/plasma/attackby(obj/item/I, mob/user, params)
var/obj/item/reagent_containers/glass/C = I
if(C.reagents.total_volume >= C.volume)
to_chat(user, "<span class='danger'>[C] is full.</span>")
return
C.reagents.add_reagent("plasma", rand(5, 10))
user.visible_message("[user] scoops some plasma from the [src] with \the [C].", "<span class='notice'>You scoop out some plasma from the [src] using \the [C].</span>")
/turf/open/lava/plasma/burn_stuff(AM)
. = 0
if(is_safe())
return FALSE
var/thing_to_check = src
if (AM)
thing_to_check = list(AM)
for(var/thing in thing_to_check)
if(isobj(thing))
var/obj/O = thing
if((O.resistance_flags & (FREEZE_PROOF)) || O.throwing)
continue
else if (isliving(thing))
. = 1
var/mob/living/L = thing
if(L.movement_type & FLYING)
continue //YOU'RE FLYING OVER IT
if("snow" in L.weather_immunities)
continue
var/buckle_check = L.buckling
if(!buckle_check)
buckle_check = L.buckled
if(isobj(buckle_check))
var/obj/O = buckle_check
if(O.resistance_flags & FREEZE_PROOF)
continue
else if(isliving(buckle_check))
var/mob/living/live = buckle_check
if("snow" in live.weather_immunities)
continue
L.adjustFireLoss(2)
if(L)
L.adjust_fire_stacks(20) //dipping into a stream of plasma would probably make you more flammable than usual
L.adjust_bodytemperature(-rand(50,65)) //its cold, man
if(ishuman(L))//are they a carbon?
var/list/plasma_parts = list()//a list of the organic parts to be turned into plasma limbs
var/list/robo_parts = list()//keep a reference of robotic parts so we know if we can turn them into a plasmaman
var/mob/living/carbon/human/PP = L
var/S = PP.dna.species
if(istype(S, /datum/species/plasmaman) || istype(S, /datum/species/android) || istype(S, /datum/species/synth)) //ignore plasmamen/robotic species
continue
for(var/BP in PP.bodyparts)
var/obj/item/bodypart/NN = BP
if(NN.status == BODYPART_ORGANIC && NN.species_id != "plasmaman") //getting every organic, non-plasmaman limb (augments/androids are immune to this)
plasma_parts += NN
if(NN.status == BODYPART_ROBOTIC)
robo_parts += NN
if(prob(35)) //checking if the delay is over & if the victim actually has any parts to nom
PP.adjustToxLoss(15)
PP.adjustFireLoss(25)
if(plasma_parts.len)
var/obj/item/bodypart/NB = pick(plasma_parts) //using the above-mentioned list to get a choice of limbs for dismember() to use
PP.emote("scream")
NB.species_id = "plasmaman"//change the species_id of the limb to that of a plasmaman
NB.no_update = TRUE
NB.change_bodypart_status()
PP.visible_message("<span class='warning'>[L] screams in pain as [L.p_their()] [NB] melts down to the bone!</span>", \
"<span class='userdanger'>You scream out in pain as your [NB] melts down to the bone, leaving an eerie plasma-like glow where flesh used to be!</span>")
if(!plasma_parts.len && !robo_parts.len) //a person with no potential organic limbs left AND no robotic limbs, time to turn them into a plasmaman
PP.IgniteMob()
PP.set_species(/datum/species/plasmaman)
PP.visible_message("<span class='warning'>[L] bursts into a brilliant purple flame as [L.p_their()] entire body is that of a skeleton!</span>", \
"<span class='userdanger'>Your senses numb as all of your remaining flesh is turned into a purple slurry, sloshing off your body and leaving only your bones to show in a vibrant purple!</span>")
/obj/vehicle/ridden/lavaboat/plasma
name = "plasma boat"
desc = "A boat used for traversing the streams of plasma without turning into an icecube."
icon_state = "goliath_boat"
icon = 'icons/obj/lavaland/dragonboat.dmi'
resistance_flags = FREEZE_PROOF
can_buckle = TRUE
/////////// papers
/obj/item/paper/crumpled/ruins/snowdin/foreshadowing
name = "scribbled note"
info = {"Something's gone VERY wrong here. Jouslen has been mumbling about some weird shit in his cabin during the night and he seems always tired when we're working. I tried to confront him about it and he blew up on me,
telling me to mind my own business. I reported him to the officer, said he'd look into it. We only got another 2 months here before we're pulled for another assignment, so this shit can't go any quicker..."}
/obj/item/paper/crumpled/ruins/snowdin/misc1
name = "Mission Prologue"
info = {"Holy shit, what a rush! Those Nanotrasen bastards didn't even know what hit 'em! All five of us dropped in right on the captain, didn't even have time to yell! We were in and out with that disk in mere minutes!
Crew didn't even know what was happening till the delta alert went down and by then we were already gone. We got a case to drink on the way home to celebrate, fuckin' job well done!"}
/obj/item/paper/crumpled/ruins/snowdin/dontdeadopeninside
name = "scribbled note"
info = {"If you're reading this: GET OUT! The mining go on here has unearthed something that was once-trapped by the layers of ice on this hell-hole. The overseer and Jouslen have gone missing. The officer is
keeping the rest of us on lockdown and I swear to god I keep hearing strange noises outside the walls at night. The gateway link has gone dead and without a supply of resources from Central, we're left
for dead here. We haven't heard anything back from the mining squad either, so I can only assume whatever the fuck they unearthed got them first before coming for us. I don't want to die here..."}
/obj/item/paper/fluff/awaymissions/snowdin/saw_usage
name = "SAW Usage"
info = "YOU SEEN IVAN, WHEN YOU HOLD SAAW LIKE PEESTOL, YOU STRONGER THAN RECOIL FOR FEAR OF HITTING FACE!"
/obj/item/paper/fluff/awaymissions/snowdin/research_feed
name = "Research Feed"
info = {"<i>A page full of graphs and other detailed information on the seismic activity of the surrounding area.</i>"}
//profile of each of the old crewmembers for the outpost
/obj/item/paper/fluff/awaymissions/snowdin/profile/overseer
name = "Personnel Record AOP#01"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Caleb Reed<br><b>Age:</b>38<br><b>Gender:</b>Male<br><b>On-Site Profession:</b>Outpost Overseer<br><br><center><b>Information</b></center><br><center>Caleb Reed lead several expeditions
among uncharted planets in search of plasma for Nanotrasen, scouring from hot savanas to freezing arctics. Track record is fairly clean with only incidient including the loss of two researchers during the
expedition of <b>_______</b>, where mis-used of explosive ordinance for tunneling causes a cave-in."}
/obj/item/paper/fluff/awaymissions/snowdin/profile/sec1
name = "Personnel Record AOP#02"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>James Reed<br><b>Age:</b>43<br><b>Gender:</b>Male<br><b>On-Site Profession:</b>Outpost Security<br><br><center><b>Information</b></center><br><center>James Reed has been a part
of Nanotrasen's security force for over 20 years, first joining in 22XX. A clean record and unwavering loyalty to the corperation through numerous deployments to various sites makes him a valuable asset to Natotrasen
when it comes to keeping the peace while prioritizing Nanotrasen privacy matters. "}
/obj/item/paper/fluff/awaymissions/snowdin/profile/hydro1
name = "Personnel Record AOP#03"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Katherine Esterdeen<br><b>Age:</b>27<br><b>Gender:</b>Female<br><b>On-Site Profession:</b>Outpost Botanist<br><br><center><b>Information</b></center><br><center>Katherine Esterdeen is a recent
graduate with a major in Botany and a PH.D in Ecology. Having a clean record and eager to work, Esterdeen seems to be the right fit for maintaining plants in the middle of nowhere."}
/obj/item/paper/fluff/awaymissions/snowdin/profile/engi1
name = "Personnel Record AOP#04"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Rachel Migro<br><b>Age:</b>35<br><b>Gender:</b>Female<br><b>On-Site Profession:</b>Outpost Engineer<br><br><center><b>Information</b></center><br><center>Recently certified to be a full-time Journeyman, Rachel has
been assigned various construction projects in the past 5 years. Competent and has no past infractions, should be of little concern."}
/obj/item/paper/fluff/awaymissions/snowdin/profile/research1
name = "Personnel Record AOP#05"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Jacob Ullman<br><b>Age:</b>27<br><b>Gender:</b>Male<br><b>On-Site Profession:</b>Outpost Researcher<br><br><center><b>Information</b></center><br><center>"}
/obj/item/paper/fluff/awaymissions/snowdin/profile/research2
name = "Personnel Record AOP#06"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Elizabeth Queef<br><b>Age:</b>28<br><b>Gender:</b>Female<br><b>On-Site Profession:</b>Outpost Researcher<br><br><center><b>Information</b></center><br><center>"}
/obj/item/paper/fluff/awaymissions/snowdin/profile/research3
name = "Personnel Record AOP#07"
info = {"<b><center>Personnel Log</b></center><br><br><b>Name:</b>Jouslen McGee<br><b>Age:</b>38<br><b>Gender:</b>Male<br><b>On-Site Profession:</b>Outpost Researcher<br><br><center><b>Information</b></center><br><center>"}
/obj/item/paper/fluff/awaymissions/snowdin/secnotice
name = "Security Notice"
info = {"YOu have been assigned to this Arctic Post with intention of protecting Nanotrasen assets and ensuring vital information is kept secure while the stationed crew obeys protocol. The picked
staff for this post have been pre-screened with no prior incidients on record, but incase of an issue you have been given a single holding cell and instructions to contact Central to terminate the
offending crewmember."}
/obj/item/paper/fluff/awaymissions/snowdin/mining
name = "Assignment Notice"
info = {"This cold-ass planet is the new-age equivalent of striking gold. Huge deposits of plasma and literal streams of plasma run through the caverns under all this ice and we're here to mine it all.\
Nanotrasen pays by the pound, so get minin' boys!"}
/obj/item/paper/crumpled/ruins/snowdin/lootstructures
name = "scribbled note"
info = {"There's some ruins scattered along the cavern, their walls seem to be made of some sort of super-condensed mixture of ice and snow. We've already barricaded up the ones we've found so far,
since we keep hearing some strange noises from inside. Besides, what sort of fool would wrecklessly run into ancient ruins full of monsters for some old gear, anyway?"}
/obj/item/paper/crumpled/ruins/snowdin/shovel
name = "shoveling duties"
info = {"Snow piles up bad here all-year round, even worse during the winter months. Keeping a constant rotation of shoveling that shit out of the way of the airlocks and keeping the paths decently clear
is a good step towards not getting stuck walking through knee-deep snow."}
//holo disk recording//--
/obj/item/disk/holodisk/snowdin/weregettingpaidright
name = "Conversation #AOP#23"
preset_image_type = /datum/preset_holoimage/researcher
preset_record_text = {"
NAME Jacob Ullman
DELAY 10
SAY Have you gotten anything interesting on the scanners yet? The deep-drilling from the plasma is making it difficult to get anything that isn't useless noise.
DELAY 45
NAME Elizabeth Queef
DELAY 10
SAY Nah. I've been feeding the AI the results for the past 2 weeks to sift through the garbage and haven't seen anything out of the usual, at least whatever Nanotrasen is looking for.
DELAY 45
NAME Jacob Ullman
DELAY 10
SAY Figured as much. Dunno what Nanotrasen expects to find out here past the plasma. At least we're getting paid to fuck around for a couple months while the AI does the hard work.
DELAY 45
NAME Elizabeth Queef
DELAY 10
SAY . . .
DELAY 10
SAY ..We're getting paid?
DELAY 20
NAME Jacob Ullman
DELAY 10
SAY ..We are getting paid, aren't we..?
DELAY 15
PRESET /datum/preset_holoimage/captain
NAME Caleb Reed
DELAY 10
SAY Paid in experience! That's the Nanotrasen Motto!
DELAY 30;"}
/obj/item/disk/holodisk/snowdin/welcometodie
name = "Conversation #AOP#1"
preset_image_type = /datum/preset_holoimage/corgi
preset_record_text = {"
NAME Friendly AI Unit
DELAY 10
SAY Hello! Welcome to the Arctic Post *338-3**$$!
DELAY 30
SAY You have been selected out of $)@! potential candidates for this post!
DELAY 30
SAY Nanotrasen is pleased to have you working in one of the many top-of-the-line research posts within the $%@!! sector!
DELAY 30
SAY Further job assignment information can be found at your local security post! Have a secure day!
DELAY 20;"}
/obj/item/disk/holodisk/snowdin/overrun
name = "Conversation #AOP#55"
preset_image_type = /datum/preset_holoimage/nanotrasenprivatesecurity
preset_record_text = {"
NAME James Reed
DELAY 10
SAY Jesus christ, what is that thing??
DELAY 30
PRESET /datum/preset_holoimage/researcher
NAME Elizabeth Queef
DELAY 10
SAY Hell if I know! Just shoot it already!
DELAY 30
PRESET /datum/preset_holoimage/nanotrasenprivatesecurity
NAME James Reed
DELAY 10
SOUND sound/weapons/laser.ogg
DELAY 10
SOUND sound/weapons/laser.ogg
DELAY 10
SOUND sound/weapons/laser.ogg
DELAY 10
SOUND sound/weapons/laser.ogg
DELAY 15
SAY Just go! I'll keep it busy, there's an outpost south of here with an elevator to the surface.
NAME Jacob Ullman
PRESET /datum/preset_holoimage/researcher.
DELAY 15
Say I don't have to be told twice! Let's get the fuck out of here.
DELAY 20;"}
/obj/item/disk/holodisk/snowdin/ripjacob
name = "Conversation #AOP#62"
preset_image_type = /datum/preset_holoimage/researcher
preset_record_text = {"
NAME Jacob Ullman
DELAY 10
SAY Get the elevator called. We got no idea how many of those fuckers are down here and I'd rather get off this planet as soon as possible.
DELAY 45
NAME Elizabeth Queef
DELAY 10
SAY You don't need to tell me twice, I just need to swipe access and then..
DELAY 15
SOUND sound/effects/glassbr1.ogg
DELAY 10
SOUND sound/effects/glassbr2.ogg
DELAY 15
NAME Jacob Ullman
DELAY 10
SAY What the FUCK was that?
DELAY 20
SAY OH FUCK THERE'S MORE OF THEM. CALL FASTER JESUS CHRIST.
DELAY 20
NAME Elizabeth Queef
DELAY 10
SAY DON'T FUCKING RUSH ME ALRIGHT IT'S BEING CALLED.
DELAY 15
SOUND sound/effects/huuu.ogg
DELAY 5
SOUND sound/effects/huuu.ogg
DELAY 15
SOUND sound/effects/woodhit.ogg
DELAY 2
SOUND sound/effects/bodyfall3.ogg
DELAY 5
SOUND sound/effects/meow1.ogg
DELAY 15
NAME Jacob Ullman
DELAY 15
SAY OH FUCK IT'S GOT ME JESUS CHRIIIiiii-
NAME Elizabeth Queef
SAY AAAAAAAAAAAAAAAA FUCK THAT
DELAY 15;"}
//lootspawners//--
/obj/effect/spawner/lootdrop/snowdin
name = "why are you using this dummy"
lootdoubles = 0
lootcount = 1
loot = list(/obj/item/bikehorn = 100)
/obj/effect/spawner/lootdrop/snowdin/dungeonlite
name = "dungeon lite"
loot = list(/obj/item/melee/classic_baton = 11,
/obj/item/melee/classic_baton/telescopic = 12,
/obj/item/book/granter/spell/smoke = 10,
/obj/item/book/granter/spell/blind = 10,
/obj/item/storage/firstaid/regular = 45,
/obj/item/storage/firstaid/toxin = 35,
/obj/item/storage/firstaid/brute = 27,
/obj/item/storage/firstaid/fire = 27,
/obj/item/storage/toolbox/syndicate = 12,
/obj/item/grenade/plastic/c4 = 7,
/obj/item/grenade/clusterbuster/smoke = 15,
/obj/item/clothing/under/chameleon = 13,
/obj/item/clothing/shoes/chameleon/noslip = 10,
/obj/item/borg/upgrade/ddrill = 3,
/obj/item/borg/upgrade/soh = 3)
/obj/effect/spawner/lootdrop/snowdin/dungeonmid
name = "dungeon mid"
loot = list(/obj/item/defibrillator/compact = 6,
/obj/item/storage/firstaid/tactical = 35,
/obj/item/shield/energy = 6,
/obj/item/shield/riot/tele = 12,
/obj/item/dnainjector/lasereyesmut = 7,
/obj/item/gun/magic/wand/fireball/inert = 3,
/obj/item/pneumatic_cannon = 15,
/obj/item/melee/transforming/energy/sword = 7,
/obj/item/book/granter/spell/knock = 15,
/obj/item/book/granter/spell/summonitem = 20,
/obj/item/book/granter/spell/forcewall = 17,
/obj/item/storage/backpack/holding = 12,
/obj/item/grenade/spawnergrenade/manhacks = 6,
/obj/item/grenade/spawnergrenade/spesscarp = 7,
/obj/item/grenade/clusterbuster/inferno = 3,
/obj/item/stack/sheet/mineral/diamond{amount = 15} = 10,
/obj/item/stack/sheet/mineral/uranium{amount = 15} = 10,
/obj/item/stack/sheet/mineral/plasma{amount = 15} = 10,
/obj/item/stack/sheet/mineral/gold{amount = 15} = 10,
/obj/item/book/granter/spell/barnyard = 4,
/obj/item/pickaxe/drill/diamonddrill = 6,
/obj/item/borg/upgrade/vtec = 7,
/obj/item/borg/upgrade/disablercooler = 7)
/obj/effect/spawner/lootdrop/snowdin/dungeonheavy
name = "dungeon heavy"
loot = list(/obj/item/twohanded/singularityhammer = 25,
/obj/item/twohanded/mjollnir = 10,
/obj/item/twohanded/fireaxe = 25,
/obj/item/organ/brain/alien = 17,
/obj/item/twohanded/dualsaber = 15,
/obj/item/organ/heart/demon = 7,
/obj/item/gun/ballistic/automatic/c20r/unrestricted = 16,
/obj/item/gun/magic/wand/resurrection/inert = 15,
/obj/item/gun/magic/wand/resurrection = 10,
/obj/item/uplink/old = 2,
/obj/item/book/granter/spell/charge = 12,
/obj/item/grenade/clusterbuster/spawner_manhacks = 15,
/obj/item/book/granter/spell/fireball = 10,
/obj/item/pickaxe/drill/jackhammer = 30,
/obj/item/borg/upgrade/syndicate = 13,
/obj/item/borg/upgrade/selfrepair = 17)
/obj/effect/spawner/lootdrop/snowdin/dungeonmisc
name = "dungeon misc"
lootdoubles = 2
lootcount = 1
loot = list(/obj/item/stack/sheet/mineral/snow{amount = 25} = 10,
/obj/item/toy/snowball = 15,
/obj/item/shovel = 10,
/obj/item/twohanded/spear = 8,
)
//special items//--
/obj/structure/barricade/wooden/snowed
name = "crude plank barricade"
desc = "This space is blocked off by a wooden barricade. It seems to be covered in a layer of snow."
icon_state = "woodenbarricade-snow"
max_integrity = 125
/obj/item/clothing/under/syndicate/coldres
name = "insulated tactical turtleneck"
desc = "A nondescript and slightly suspicious-looking turtleneck with digital camouflage cargo pants. The interior has been padded with special insulation for both warmth and protection."
armor = list("melee" = 20, "bullet" = 10, "laser" = 0,"energy" = 5, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 25, "acid" = 25)
cold_protection = CHEST|GROIN|ARMS|LEGS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
/obj/item/clothing/shoes/combat/coldres
name = "insulated combat boots"
desc = "High speed, low drag combat boots, now with an added layer of insulation."
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
/obj/item/gun/magic/wand/fireball/inert
name = "weakened wand of fireball"
desc = "This wand shoots scorching balls of fire that explode into destructive flames. The years of the cold have weakened the magic inside the wand."
max_charges = 4
/obj/item/gun/magic/wand/resurrection/inert
name = "weakened wand of healing"
desc = "This wand uses healing magics to heal and revive. The years of the cold have weakened the magic inside the wand."
max_charges = 5
/obj/effect/mob_spawn/human/syndicatesoldier/coldres
name = "Syndicate Snow Operative"
outfit = /datum/outfit/snowsyndie/corpse
/datum/outfit/snowsyndie/corpse
name = "Syndicate Snow Operative Corpse"
implants = null
/obj/effect/mob_spawn/human/syndicatesoldier/coldres/alive
name = "sleeper"
mob_name = "Syndicate Snow Operative"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
roundstart = FALSE
death = FALSE
job_description = "Syndicate Snow Operative"
faction = ROLE_SYNDICATE
outfit = /datum/outfit/snowsyndie
flavour_text = "<span class='big bold'>You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \
disposed of swiftly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the caves can be a deadly place even for a trained operative such as yourself.</span>"
/datum/outfit/snowsyndie
name = "Syndicate Snow Operative"
uniform = /obj/item/clothing/under/syndicate/coldres
shoes = /obj/item/clothing/shoes/combat/coldres
ears = /obj/item/radio/headset/syndicate/alt
r_pocket = /obj/item/gun/ballistic/automatic/pistol
id = /obj/item/card/id/syndicate
implants = list(/obj/item/implant/exile)
/datum/outfit/vr/snowtide
name = "Snowdin Outfit"
shoes = /obj/item/clothing/shoes/winterboots
suit = /obj/item/clothing/suit/hooded/wintercoat
back = /obj/item/storage/backpack
mask = /obj/item/clothing/mask/breath
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
internals_slot = SLOT_R_STORE
/obj/effect/mob_spawn/human/syndicatesoldier/coldres/alive/female
mob_gender = FEMALE
//mobs//--
//ice spiders moved to giant_spiders.dm
//objs//--
/obj/structure/flora/rock/icy
name = "icy rock"
color = rgb(204,233,235)
/obj/structure/flora/rock/pile/icy
name = "icey rocks"
color = rgb(204,233,235)
//decals//--
/obj/effect/turf_decal/snowdin_station_sign
icon_state = "AOP1"
/obj/effect/turf_decal/snowdin_station_sign/two
icon_state = "AOP2"
/obj/effect/turf_decal/snowdin_station_sign/three
icon_state = "AOP3"
/obj/effect/turf_decal/snowdin_station_sign/four
icon_state = "AOP4"
/obj/effect/turf_decal/snowdin_station_sign/five
icon_state = "AOP5"
/obj/effect/turf_decal/snowdin_station_sign/six
icon_state = "AOP6"
/obj/effect/turf_decal/snowdin_station_sign/seven
icon_state = "AOP7"
/obj/effect/turf_decal/snowdin_station_sign/up
icon_state = "AOPU1"
/obj/effect/turf_decal/snowdin_station_sign/up/two
icon_state = "AOPU2"
/obj/effect/turf_decal/snowdin_station_sign/up/three
icon_state = "AOPU3"
/obj/effect/turf_decal/snowdin_station_sign/up/four
icon_state = "AOPU4"
/obj/effect/turf_decal/snowdin_station_sign/up/five
icon_state = "AOPU5"
/obj/effect/turf_decal/snowdin_station_sign/up/six
icon_state = "AOPU6"
/obj/effect/turf_decal/snowdin_station_sign/up/seven
icon_state = "AOPU7"
/obj/effect/landmark/vr_spawn/snowdin
vr_outfit = /datum/outfit/vr/snowtide
-43
View File
@@ -1,43 +0,0 @@
/world/proc/_BSQL_Internal_Call(func, ...)
var/list/call_args = args.Copy(2)
BSQL_Debug("_BSQL_Internal_Call(): [args[1]]([call_args.Join(", ")])")
. = call(_BSQL_Library_Path(), func)(arglist(call_args))
BSQL_Debug("Result: [. == null ? "NULL" : "\"[.]\""]")
/world/proc/_BSQL_Library_Path()
return system_type == MS_WINDOWS ? "BSQL.dll" : "libBSQL.so"
/world/proc/_BSQL_InitCheck(datum/BSQL_Connection/caller)
var/static/library_initialized = FALSE
if(_BSQL_Initialized())
return
var/libPath = _BSQL_Library_Path()
if(!fexists(libPath))
BSQL_DEL_CALL(caller)
BSQL_ERROR("Could not find [libPath]!")
return
var/version = _BSQL_Internal_Call("Version")
if(version != BSQL_VERSION)
BSQL_DEL_CALL(caller)
BSQL_ERROR("BSQL DMAPI version mismatch! Expected [BSQL_VERSION], got [version == null ? "NULL" : version]!")
return
var/result = _BSQL_Internal_Call("Initialize")
if(result)
BSQL_DEL_CALL(caller)
BSQL_ERROR(result)
return
_BSQL_Initialized(TRUE)
/world/proc/_BSQL_Initialized(new_val)
var/static/bsql_library_initialized = FALSE
if(new_val != null)
bsql_library_initialized = new_val
return bsql_library_initialized
/world/BSQL_Shutdown()
if(!_BSQL_Initialized())
return
_BSQL_Internal_Call("Shutdown")
_BSQL_Initialized(FALSE)

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