mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-30 07:27:57 +01:00
fixes merge conflict with floorcluwne/admin topic.dm
This commit is contained in:
@@ -65,7 +65,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
|
||||
var/computerid
|
||||
var/ip
|
||||
|
||||
if(ismob(banned_mob))
|
||||
if(ismob(banned_mob) && banned_mob.ckey)
|
||||
ckey = banned_mob.ckey
|
||||
if(banned_mob.client)
|
||||
computerid = banned_mob.client.computer_id
|
||||
@@ -74,6 +74,12 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
|
||||
ckey = ckey(banckey)
|
||||
computerid = bancid
|
||||
ip = banip
|
||||
else if(ismob(banned_mob))
|
||||
message_admins("<font color='red'>[key_name_admin(usr)] attempted to add a ban based on a ckey-less mob, with no ckey provided. Report this bug.",1)
|
||||
return
|
||||
else
|
||||
message_admins("<font color='red'>[key_name_admin(usr)] attempted to add a ban based on a non-existent mob, with no ckey provided. Report this bug.",1)
|
||||
return
|
||||
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
|
||||
query.Execute()
|
||||
@@ -82,7 +88,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
|
||||
validckey = 1
|
||||
if(!validckey)
|
||||
if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key)))
|
||||
message_admins("<font color='red'>[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.</font>",1)
|
||||
message_admins("<font color='red'>[key_name_admin(usr)] attempted to ban [ckey], but [ckey] does not exist in the player database. Please only ban actual players.</font>",1)
|
||||
return
|
||||
|
||||
var/a_ckey
|
||||
|
||||
@@ -29,6 +29,20 @@ var/global/nologevent = 0
|
||||
to_chat(C, msg)
|
||||
|
||||
|
||||
/proc/admin_ban_mobsearch(var/mob/M, var/ckey_to_find, var/mob/admin_to_notify)
|
||||
if(!M || !M.ckey)
|
||||
if(ckey_to_find)
|
||||
for(var/mob/O in GLOB.mob_list)
|
||||
if(O.ckey && O.ckey == ckey_to_find)
|
||||
if(admin_to_notify)
|
||||
to_chat(admin_to_notify, "<span class='warning'>admin_ban_mobsearch: Player [ckey_to_find] is now in mob [O]. Pulling data from new mob.</span>")
|
||||
return O
|
||||
if(admin_to_notify)
|
||||
to_chat(admin_to_notify, "<span class='warning'>admin_ban_mobsearch: Player [ckey_to_find] does not seem to have any mob, anywhere. This is probably an error.</span>")
|
||||
else if(admin_to_notify)
|
||||
to_chat(admin_to_notify, "<span class='warning'>admin_ban_mobsearch: No mob or ckey detected.</span>")
|
||||
return M
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////Panels
|
||||
|
||||
/datum/admins/proc/show_player_panel(var/mob/M in GLOB.mob_list)
|
||||
@@ -58,8 +72,9 @@ var/global/nologevent = 0
|
||||
body += "<br><br>\[ "
|
||||
body += "<a href='?_src_=vars;Vars=[M.UID()]'>VV</a> - "
|
||||
body += "[ADMIN_TP(M,"TP")] - "
|
||||
body += "<a href='?src=[usr.UID()];priv_msg=[M.UID()]'>PM</a> - "
|
||||
body += "[ADMIN_SM(M,"SM")] - "
|
||||
if(M.client)
|
||||
body += "<a href='?src=[usr.UID()];priv_msg=[M.UID()]'>PM</a> - "
|
||||
body += "[ADMIN_SM(M,"SM")] - "
|
||||
if(ishuman(M) && M.mind)
|
||||
body += "<a href='?_src_=holder;HeadsetMessage=[M.UID()]'>HM</a> -"
|
||||
body += "[admin_jump_link(M)]\] </b><br>"
|
||||
@@ -70,12 +85,13 @@ var/global/nologevent = 0
|
||||
if(M.client.related_accounts_ip.len)
|
||||
body += "<b>Related accounts by IP:</b> [jointext(M.client.related_accounts_ip, " - ")]<br><br>"
|
||||
|
||||
body += "<A href='?_src_=holder;boot2=[M.UID()]'>Kick</A> | "
|
||||
body += "<A href='?_src_=holder;warn=[M.ckey]'>Warn</A> | "
|
||||
body += "<A href='?_src_=holder;newban=[M.UID()]'>Ban</A> | "
|
||||
body += "<A href='?_src_=holder;jobban2=[M.UID()]'>Jobban</A> | "
|
||||
body += "<A href='?_src_=holder;appearanceban=[M.UID()]'>Appearance Ban</A> | "
|
||||
body += "<A href='?_src_=holder;shownoteckey=[M.ckey]'>Notes</A> | "
|
||||
if(M.ckey)
|
||||
body += "<A href='?_src_=holder;boot2=[M.UID()]'>Kick</A> | "
|
||||
body += "<A href='?_src_=holder;warn=[M.ckey]'>Warn</A> | "
|
||||
body += "<A href='?_src_=holder;newban=[M.UID()];dbbanaddckey=[M.ckey]'>Ban</A> | "
|
||||
body += "<A href='?_src_=holder;jobban2=[M.UID()];dbbanaddckey=[M.ckey]'>Jobban</A> | "
|
||||
body += "<A href='?_src_=holder;appearanceban=[M.UID()];dbbanaddckey=[M.ckey]'>Appearance Ban</A> | "
|
||||
body += "<A href='?_src_=holder;shownoteckey=[M.ckey]'>Notes</A> | "
|
||||
if(M.client)
|
||||
if(M.client.check_watchlist(M.client.ckey))
|
||||
body += "<A href='?_src_=holder;watchremove=[M.ckey]'>Remove from Watchlist</A> | "
|
||||
@@ -115,7 +131,9 @@ var/global/nologevent = 0
|
||||
body += {" | <A href='?_src_=holder;Bless=[M.UID()]'>Bless</A> | <A href='?_src_=holder;Smite=[M.UID()]'>Smite</A>"}
|
||||
|
||||
if(isLivingSSD(M))
|
||||
if(!istype(M.loc, /obj/machinery/cryopod))
|
||||
if(istype(M.loc, /obj/machinery/cryopod))
|
||||
body += {" | <A href='?_src_=holder;cryossd=[M.UID()]'>De-Spawn</A> "}
|
||||
else
|
||||
body += {" | <A href='?_src_=holder;cryossd=[M.UID()]'>Cryo</A> "}
|
||||
|
||||
if(M.client)
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
investigate_log_subjects |= subject
|
||||
F << "<small>[time_stamp()] \ref[src] ([x],[y],[z])</small> || [src] [message]<br>"
|
||||
|
||||
/proc/log_investigate(message, subject)
|
||||
if(!message) return
|
||||
var/F = investigate_subject2file(subject)
|
||||
if(!F) return
|
||||
investigate_log_subjects |= subject
|
||||
F << "<small>[time_stamp()] || [message]<br>"
|
||||
|
||||
//ADMINVERBS
|
||||
/client/proc/investigate_show( subject in investigate_log_subjects )
|
||||
set name = "Investigate"
|
||||
|
||||
@@ -77,6 +77,7 @@ var/list/admin_verbs_admin = list(
|
||||
/client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/
|
||||
/client/proc/list_ssds,
|
||||
/client/proc/cmd_admin_headset_message,
|
||||
/client/proc/spawn_floor_cluwne,
|
||||
)
|
||||
var/list/admin_verbs_ban = list(
|
||||
/client/proc/unban_panel,
|
||||
@@ -720,7 +721,7 @@ var/list/admin_verbs_ticket = list(
|
||||
var/list/Lines = file2list("config/admins.txt")
|
||||
for(var/line in Lines)
|
||||
var/list/splitline = splittext(line, " - ")
|
||||
if(n_lower(splitline[1]) == ckey)
|
||||
if(lowertext(splitline[1]) == ckey)
|
||||
if(splitline.len >= 2)
|
||||
rank = ckeyEx(splitline[2])
|
||||
break
|
||||
|
||||
+69
-39
@@ -433,6 +433,7 @@
|
||||
if(!M.ckey) //sanity
|
||||
to_chat(usr, "This mob has no ckey")
|
||||
return
|
||||
var/ban_ckey_param = href_list["dbbanaddckey"]
|
||||
|
||||
var/banreason = appearance_isbanned(M)
|
||||
if(banreason)
|
||||
@@ -455,6 +456,7 @@
|
||||
var/reason = input(usr,"Please state the reason","Reason") as message|null
|
||||
if(!reason)
|
||||
return
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]")
|
||||
log_admin("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]")
|
||||
feedback_inc("ban_appearance",1)
|
||||
@@ -501,17 +503,17 @@
|
||||
//Regular jobs
|
||||
//Command (Blue)
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr align='center' bgcolor='ccccff'><th colspan='[length(command_positions)]'><a href='?src=[UID()];jobban3=commanddept;jobban4=[M.UID()]'>Command Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr align='center' bgcolor='ccccff'><th colspan='[length(command_positions)]'><a href='?src=[UID()];jobban3=commanddept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Command Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in command_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 6) //So things dont get squiiiiished!
|
||||
@@ -522,17 +524,17 @@
|
||||
//Security (Red)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='ffddf0'><th colspan='[length(security_positions)]'><a href='?src=[UID()];jobban3=securitydept;jobban4=[M.UID()]'>Security Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='ffddf0'><th colspan='[length(security_positions)]'><a href='?src=[UID()];jobban3=securitydept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Security Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in security_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -543,17 +545,17 @@
|
||||
//Engineering (Yellow)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='fff5cc'><th colspan='[length(engineering_positions)]'><a href='?src=[UID()];jobban3=engineeringdept;jobban4=[M.UID()]'>Engineering Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='fff5cc'><th colspan='[length(engineering_positions)]'><a href='?src=[UID()];jobban3=engineeringdept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Engineering Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in engineering_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -564,17 +566,17 @@
|
||||
//Medical (White)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='ffeef0'><th colspan='[length(medical_positions)]'><a href='?src=[UID()];jobban3=medicaldept;jobban4=[M.UID()]'>Medical Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='ffeef0'><th colspan='[length(medical_positions)]'><a href='?src=[UID()];jobban3=medicaldept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Medical Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in medical_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -585,17 +587,17 @@
|
||||
//Science (Purple)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='e79fff'><th colspan='[length(science_positions)]'><a href='?src=[UID()];jobban3=sciencedept;jobban4=[M.UID()]'>Science Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='e79fff'><th colspan='[length(science_positions)]'><a href='?src=[UID()];jobban3=sciencedept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Science Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in science_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -606,17 +608,17 @@
|
||||
//Support (Grey)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='dddddd'><th colspan='[length(support_positions)]'><a href='?src=[UID()];jobban3=supportdept;jobban4=[M.UID()]'>Support Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='dddddd'><th colspan='[length(support_positions)]'><a href='?src=[UID()];jobban3=supportdept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Support Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in support_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -627,17 +629,17 @@
|
||||
//Non-Human (Green)
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='ccffcc'><th colspan='[length(nonhuman_positions)+1]'><a href='?src=[UID()];jobban3=nonhumandept;jobban4=[M.UID()]'>Non-human Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='ccffcc'><th colspan='[length(nonhuman_positions)+1]'><a href='?src=[UID()];jobban3=nonhumandept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Non-human Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in nonhuman_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -646,29 +648,29 @@
|
||||
|
||||
//Drone
|
||||
if(jobban_isbanned(M, "Drone"))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=Drone;jobban4=[M.UID()]'><font color=red>Drone</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=Drone;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>Drone</font></a></td>"
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=Drone;jobban4=[M.UID()]'>Drone</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=Drone;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Drone</a></td>"
|
||||
|
||||
//pAI
|
||||
if(jobban_isbanned(M, "pAI"))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=pAI;jobban4=[M.UID()]'><font color=red>pAI</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=pAI;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>pAI</font></a></td>"
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=pAI;jobban4=[M.UID()]'>pAI</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=pAI;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>pAI</a></td>"
|
||||
|
||||
jobs += "</tr></table>"
|
||||
|
||||
//Antagonist (Orange)
|
||||
var/isbanned_dept = jobban_isbanned(M, "Syndicate")
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='ffeeaa'><th colspan='10'><a href='?src=[UID()];jobban3=Syndicate;jobban4=[M.UID()]'>Antagonist Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='ffeeaa'><th colspan='10'><a href='?src=[UID()];jobban3=Syndicate;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Antagonist Positions</a></th></tr><tr align='center'>"
|
||||
|
||||
counter = 0
|
||||
for(var/role in antag_roles)
|
||||
if(jobban_isbanned(M, role) || isbanned_dept)
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'><font color=red>[replacetext(role, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(role, " ", " ")]</font></a></td>"
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'>[replacetext(role, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(role, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -683,9 +685,9 @@
|
||||
counter = 0
|
||||
for(var/role in other_roles)
|
||||
if(jobban_isbanned(M, role) || isbanned_dept)
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'><font color=red>[replacetext(role, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(role, " ", " ")]</font></a></td>"
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'>[replacetext(role, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(role, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -696,17 +698,17 @@
|
||||
//Whitelisted positions
|
||||
counter = 0
|
||||
jobs += "<table cellpadding='1' cellspacing='0' width='100%'>"
|
||||
jobs += "<tr bgcolor='dddddd'><th colspan='[length(whitelisted_positions)]'><a href='?src=[UID()];jobban3=whitelistdept;jobban4=[M.UID()]'>Whitelisted Positions</a></th></tr><tr align='center'>"
|
||||
jobs += "<tr bgcolor='dddddd'><th colspan='[length(whitelisted_positions)]'><a href='?src=[UID()];jobban3=whitelistdept;jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>Whitelisted Positions</a></th></tr><tr align='center'>"
|
||||
for(var/jobPos in whitelisted_positions)
|
||||
if(!jobPos) continue
|
||||
var/datum/job/job = job_master.GetJob(jobPos)
|
||||
if(!job) continue
|
||||
|
||||
if(jobban_isbanned(M, job.title))
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(job.title, " ", " ")]</font></a></td>"
|
||||
counter++
|
||||
else
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", " ")]</a></td>"
|
||||
counter++
|
||||
|
||||
if(counter >= 5) //So things dont get squiiiiished!
|
||||
@@ -733,6 +735,8 @@
|
||||
alert("You cannot perform this action. You must be of a higher administrative rank!")
|
||||
return
|
||||
|
||||
var/ban_ckey_param = href_list["dbbanaddckey"]
|
||||
|
||||
if(!job_master)
|
||||
to_chat(usr, "Job Master has not been setup!")
|
||||
return
|
||||
@@ -800,7 +804,7 @@
|
||||
|
||||
//Banning comes first
|
||||
if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban.
|
||||
switch(alert("Temporary Ban?",,"Yes","No", "Cancel"))
|
||||
switch(alert("Temporary Ban of [M.ckey]?",,"Yes","No", "Cancel"))
|
||||
if("Yes")
|
||||
if(config.ban_legacy_system)
|
||||
to_chat(usr, "<span class='warning'>Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.</span>")
|
||||
@@ -813,6 +817,7 @@
|
||||
return
|
||||
|
||||
var/msg
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
for(var/job in notbannedlist)
|
||||
ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]")
|
||||
log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes")
|
||||
@@ -835,6 +840,7 @@
|
||||
var/reason = input(usr,"Please state the reason","Reason","") as message|null
|
||||
if(reason)
|
||||
var/msg
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
for(var/job in notbannedlist)
|
||||
ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]")
|
||||
log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]")
|
||||
@@ -959,9 +965,11 @@
|
||||
if(!check_rights(R_BAN)) return
|
||||
|
||||
var/mob/M = locateUID(href_list["newban"])
|
||||
if(!ismob(M)) return
|
||||
if(!ismob(M))
|
||||
return
|
||||
var/ban_ckey_param = href_list["dbbanaddckey"]
|
||||
|
||||
switch(alert("Temporary Ban?",,"Yes","No", "Cancel"))
|
||||
switch(alert("Temporary Ban of [M.ckey] / [ban_ckey_param]?",,"Yes","No", "Cancel"))
|
||||
if("Yes")
|
||||
var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null
|
||||
if(!mins)
|
||||
@@ -970,6 +978,7 @@
|
||||
var/reason = input(usr,"Please state the reason","Reason") as message|null
|
||||
if(!reason)
|
||||
return
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins)
|
||||
ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.")
|
||||
to_chat(M, "<span class='warning'><BIG><B>You have been banned by [usr.client.ckey].\nReason: [reason].</B></BIG></span>")
|
||||
@@ -991,10 +1000,13 @@
|
||||
if(!reason)
|
||||
return
|
||||
switch(alert(usr,"IP ban?",,"Yes","No","Cancel"))
|
||||
if("Cancel") return
|
||||
if("Cancel")
|
||||
return
|
||||
if("Yes")
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP)
|
||||
if("No")
|
||||
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
|
||||
AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0)
|
||||
to_chat(M, "<span class='warning'><BIG><B>You have been banned by [usr.client.ckey].\nReason: [reason].</B></BIG></span>")
|
||||
to_chat(M, "<span class='warning'>This is a permanent ban.</span>")
|
||||
@@ -1869,6 +1881,7 @@
|
||||
ptypes += "Assassin"
|
||||
ptypes += "Hunter"
|
||||
ptypes += "Crew Traitor"
|
||||
ptypes += "Floor Cluwne"
|
||||
var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes
|
||||
if(!(punishment in ptypes))
|
||||
return
|
||||
@@ -1978,6 +1991,12 @@
|
||||
if("Gib")
|
||||
M.gib(FALSE)
|
||||
logmsg = "gibbed."
|
||||
if("Floor Cluwne")
|
||||
logmsg = "floor cluwne"
|
||||
var/turf/T = get_turf(M)
|
||||
var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T)
|
||||
FC.smiting = TRUE
|
||||
FC.Acquire_Victim(M)
|
||||
if(logmsg)
|
||||
log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]")
|
||||
message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]")
|
||||
@@ -1991,8 +2010,12 @@
|
||||
if(!isLivingSSD(H))
|
||||
to_chat(usr, "This can only be used on living, SSD players.")
|
||||
return
|
||||
var/success = cryo_ssd(H)
|
||||
if(success)
|
||||
if(istype(H.loc, /obj/machinery/cryopod))
|
||||
var/obj/machinery/cryopod/P = H.loc
|
||||
P.despawn_occupant()
|
||||
log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.")
|
||||
message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.")
|
||||
else if(cryo_ssd(H))
|
||||
log_admin("[key_name(usr)] sent [H.job] [H] to cryo.")
|
||||
message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.")
|
||||
else if(href_list["FaxReplyTemplate"])
|
||||
@@ -3270,6 +3293,13 @@
|
||||
ticker.mode.station_goals += G
|
||||
modify_goals()
|
||||
|
||||
else if(href_list["showdetails"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/text = html_decode(href_list["showdetails"])
|
||||
usr << browse("<HTML><HEAD><TITLE>Details</TITLE></HEAD><BODY><TT>[replacetext(text, "\n", "<BR>")]</TT></BODY></HTML>",
|
||||
"window=show_details;size=500x200")
|
||||
|
||||
// Library stuff
|
||||
else if(href_list["library_book_id"])
|
||||
var/isbn = sanitizeSQL(href_list["library_book_id"])
|
||||
|
||||
@@ -937,25 +937,20 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
|
||||
/client/proc/reset_all_tcs()
|
||||
set category = "Admin"
|
||||
set name = "Reset Telecomms Scripts"
|
||||
set desc = "Blanks all telecomms scripts from all telecomms servers"
|
||||
set name = "Reset NTTC Configuration"
|
||||
set desc = "Resets NTTC to the default configuration."
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/confirm = alert(src, "You sure you want to blank all NTSL scripts?", "Confirm", "Yes", "No")
|
||||
var/confirm = alert(src, "You sure you want to reset NTTC?", "Confirm", "Yes", "No")
|
||||
if(confirm != "Yes")
|
||||
return
|
||||
|
||||
for(var/obj/machinery/telecomms/server/S in telecomms_list)
|
||||
var/datum/TCS_Compiler/C = S.Compiler
|
||||
S.rawcode = ""
|
||||
C.Compile("")
|
||||
for(var/obj/machinery/computer/telecomms/traffic/T in GLOB.machines)
|
||||
T.storedcode = ""
|
||||
log_admin("[key_name(usr)] blanked all telecomms scripts.")
|
||||
message_admins("[key_name_admin(usr)] blanked all telecomms scripts.")
|
||||
feedback_add_details("admin_verb","RAT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
GLOB.nttc_config.reset()
|
||||
log_admin("[key_name(usr)] reset NTTC scripts.")
|
||||
message_admins("[key_name_admin(usr)] reset NTTC scripts.")
|
||||
feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/list_ssds()
|
||||
set category = "Admin"
|
||||
@@ -984,15 +979,22 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
if(job_string in command_positions)
|
||||
job_string = "<U>" + job_string + "</U>"
|
||||
role_string = "-"
|
||||
var/obj_count = 0
|
||||
var/obj_string = ""
|
||||
if(H.mind)
|
||||
if(H.mind.special_role)
|
||||
role_string = "<U>[H.mind.special_role]</U>"
|
||||
if(!H.key && H.mind.key)
|
||||
key_string = H.mind.key
|
||||
msg += "<TR><TD>[key_string]</TD><TD>[H.real_name]</TD><TD>[job_string]</TD><TD>[mins_ssd]</TD><TD>[role_string]</TD>"
|
||||
for(var/datum/objective/O in all_objectives)
|
||||
if(O.target == H.mind)
|
||||
obj_count++
|
||||
if(obj_count > 0)
|
||||
obj_string = "<BR><U>Obj Target</U>"
|
||||
msg += "<TR><TD>[key_string]</TD><TD>[H.real_name]</TD><TD>[job_string]</TD><TD>[mins_ssd]</TD><TD>[role_string][obj_string]</TD>"
|
||||
msg += "<TD>[get_area(H)]</TD><TD>[ADMIN_PP(H,"PP")]</TD>"
|
||||
if(istype(H.loc, /obj/machinery/cryopod))
|
||||
msg += "<TD>In Cryo</TD>"
|
||||
msg += "<TD><A href='?_src_=holder;cryossd=[H.UID()]'>De-Spawn</A></TD>"
|
||||
else
|
||||
msg += "<TD><A href='?_src_=holder;cryossd=[H.UID()]'>Cryo</A></TD>"
|
||||
msg += "</TR>"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/client/proc/spawn_floor_cluwne()
|
||||
set category = "Event"
|
||||
set name = "Unleash Floor Cluwne"
|
||||
set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!"
|
||||
var/target
|
||||
|
||||
if(!check_rights(R_EVENT))
|
||||
return
|
||||
|
||||
var/confirm = alert("Are you sure you want to release a floor cluwne and kill alot of people?", "Confirm Massacre", "Yes", "No")
|
||||
if(confirm == "Yes")
|
||||
|
||||
var/turf/T = get_turf(usr)
|
||||
target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in GLOB.player_list
|
||||
if(isnull(target))
|
||||
return
|
||||
if(target && ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T)
|
||||
FC.Acquire_Victim(H)
|
||||
else
|
||||
new /mob/living/simple_animal/hostile/floor_cluwne(T)
|
||||
log_admin("[key_name(usr)] spawned floor cluwne.")
|
||||
message_admins("[key_name(usr)] spawned floor cluwne.")
|
||||
else
|
||||
return
|
||||
@@ -13,7 +13,7 @@
|
||||
bomb_name = "remote-control bomb"
|
||||
|
||||
var/code = 30
|
||||
var/frequency = 1457
|
||||
var/frequency = RSD_FREQ
|
||||
var/delay = 0
|
||||
var/datum/radio_frequency/radio_connection
|
||||
var/airlock_wire = null
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
var/injector_tag="inc_in"
|
||||
var/output_tag="inc_out"
|
||||
var/sensor_tag="inc_sensor"
|
||||
frequency=1449
|
||||
frequency=AIRLOCK_FREQ
|
||||
var/temperature=1000
|
||||
|
||||
/obj/machinery/computer/general_air_control/atmos_automation/burnchamber/New()
|
||||
@@ -310,7 +310,7 @@
|
||||
var/o2_injector_tag="air_o2_in"
|
||||
var/output_tag="air_out"
|
||||
var/sensor_tag="air_sensor"
|
||||
frequency=1443
|
||||
frequency=ATMOS_DISTRO_FREQ
|
||||
var/temperature=1000
|
||||
|
||||
/obj/machinery/computer/general_air_control/atmos_automation/air_mixing/New()
|
||||
|
||||
@@ -17,8 +17,8 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
//List of ALL assets for the above, format is list(filename = asset).
|
||||
/var/global/list/asset_cache = list()
|
||||
//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files
|
||||
#define ASSET_CACHE_PRELOAD_CONCURRENT 3
|
||||
|
||||
/client
|
||||
var/list/cache = list() // List of all assets sent to this client by the asset cache.
|
||||
@@ -44,13 +44,10 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
|
||||
return 0
|
||||
|
||||
client << browse_rsc(asset_cache[asset_name], asset_name)
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if(client)
|
||||
client.cache += asset_name
|
||||
client << browse_rsc(SSassets.cache[asset_name], asset_name)
|
||||
if(!verify) // Can't access the asset cache browser, rip.
|
||||
client.cache += asset_name
|
||||
return 1
|
||||
if(!client)
|
||||
return 0
|
||||
|
||||
client.sending |= asset_name
|
||||
var/job = ++client.last_asset_job
|
||||
@@ -94,15 +91,13 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
if(unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
to_chat(client, "Sending Resources...")
|
||||
for(var/asset in unreceived)
|
||||
if(asset in asset_cache)
|
||||
client << browse_rsc(asset_cache[asset], asset)
|
||||
if(asset in SSassets.cache)
|
||||
client << browse_rsc(SSassets.cache[asset], asset)
|
||||
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if(client)
|
||||
client.cache += unreceived
|
||||
if(!verify) // Can't access the asset cache browser, rip.
|
||||
client.cache += unreceived
|
||||
return 1
|
||||
if(!client)
|
||||
return 0
|
||||
|
||||
client.sending |= unreceived
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
@@ -127,30 +122,25 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
var/concurrent_tracker = 1
|
||||
for(var/file in files)
|
||||
if(!client)
|
||||
break
|
||||
if(register_asset)
|
||||
register_asset(file,files[file])
|
||||
send_asset(client,file)
|
||||
sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
|
||||
register_asset(file, files[file])
|
||||
if(concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT)
|
||||
concurrent_tracker = 1
|
||||
send_asset(client, file)
|
||||
else
|
||||
concurrent_tracker++
|
||||
send_asset(client, file, verify = FALSE)
|
||||
sleep(0) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//if it's an icon or something be careful, you'll have to copy it before further use.
|
||||
/proc/register_asset(var/asset_name, var/asset)
|
||||
asset_cache[asset_name] = asset
|
||||
|
||||
//From here on out it's populating the asset cache.
|
||||
/proc/populate_asset_cache()
|
||||
for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple))
|
||||
var/datum/asset/A = new type()
|
||||
A.register()
|
||||
|
||||
for(var/client/C in GLOB.clients)
|
||||
//doing this to a client too soon after they've connected can cause issues, also the proc we call sleeps
|
||||
spawn(10)
|
||||
getFilesSlow(C, asset_cache, FALSE)
|
||||
SSassets.cache[asset_name] = asset
|
||||
|
||||
//These datums are used to populate the asset cache, the proc "register()" does this.
|
||||
|
||||
|
||||
@@ -642,9 +642,9 @@
|
||||
'html/search.js', // Used in various non-NanoUI HTML windows for search functionality
|
||||
'html/panels.css' // Used for styling certain panels, such as in the new player panel
|
||||
)
|
||||
spawn (10)
|
||||
spawn (10) //removing this spawn causes all clients to not get verbs.
|
||||
//Precache the client with all other assets slowly, so as to not block other browse() calls
|
||||
getFilesSlow(src, asset_cache, register_asset = FALSE)
|
||||
getFilesSlow(src, SSassets.preload, register_asset = FALSE)
|
||||
|
||||
//For debugging purposes
|
||||
/client/proc/list_all_languages()
|
||||
|
||||
@@ -5,6 +5,24 @@
|
||||
var/obj/item/clothing/head/hood
|
||||
var/hoodtype = /obj/item/clothing/head/winterhood //so the chaplain hoodie or other hoodies can override this
|
||||
|
||||
/obj/item/clothing/head/chaplain_hood
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/head/nun_hood
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/head/monk_hood
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/head/culthood
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/head/culthood/alt
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/head/berserkerhood
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/suit/hooded/New()
|
||||
MakeHood()
|
||||
..()
|
||||
|
||||
@@ -195,7 +195,8 @@ var/list/event_last_fired = list()
|
||||
new /datum/event_meta/alien(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 0, list(ASSIGNMENT_SECURITY = 15), 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 0, is_one_shot = 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 0, list(ASSIGNMENT_SECURITY = 15), is_one_shot = 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 15, is_one_shot = 1)
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 15, is_one_shot = 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = 1)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/datum/event/spawn_floor_cluwne
|
||||
|
||||
|
||||
/datum/event/spawn_floor_cluwne/start()
|
||||
|
||||
if(!xeno_spawn)
|
||||
message_admins("No valid spawn locations found, aborting...")
|
||||
return kill()
|
||||
|
||||
var/turf/T = get_turf(pick(xeno_spawn))
|
||||
var/mob/living/simple_animal/hostile/floor_cluwne/S = new(T)
|
||||
playsound(S, 'sound/spookoween/scary_horn.ogg', 50, 1, -1)
|
||||
message_admins("A floor cluwne has been spawned at [COORD(T)][ADMIN_JMP(T)]")
|
||||
log_game("A floor cluwne has been spawned at [COORD(T)]")
|
||||
return 1
|
||||
+11
-15
@@ -220,24 +220,20 @@
|
||||
if(M.incapacitated() || !Adjacent(M))
|
||||
return
|
||||
|
||||
if(over_object == M)
|
||||
if(over_object == M || istype(over_object, /obj/screen))
|
||||
if(!remove_item_from_storage(M))
|
||||
M.unEquip(src)
|
||||
M.put_in_hands(src)
|
||||
if(over_object != M)
|
||||
switch(over_object.name)
|
||||
if("r_hand")
|
||||
M.put_in_r_hand(src)
|
||||
if("l_hand")
|
||||
M.put_in_l_hand(src)
|
||||
else
|
||||
M.put_in_hands(src)
|
||||
|
||||
else if(istype(over_object, /obj/screen))
|
||||
switch(over_object.name)
|
||||
if("r_hand")
|
||||
if(!remove_item_from_storage(M))
|
||||
M.unEquip(src)
|
||||
M.put_in_r_hand(src)
|
||||
if("l_hand")
|
||||
if(!remove_item_from_storage(M))
|
||||
M.unEquip(src)
|
||||
M.put_in_l_hand(src)
|
||||
|
||||
add_fingerprint(M)
|
||||
usr.visible_message("<span class='notice'>[usr] picks up the deck.</span>")
|
||||
add_fingerprint(M)
|
||||
usr.visible_message("<span class='notice'>[usr] picks up the deck.</span>")
|
||||
|
||||
/obj/item/pack
|
||||
name = "Card Pack"
|
||||
|
||||
@@ -34,6 +34,31 @@
|
||||
// diag_hud_set_health()
|
||||
// update_health_hud()
|
||||
|
||||
/mob/living/silicon/robot/SetStunned(amount, updating = 1, force = 0) //if you REALLY need to set stun to a set amount without the whole "can't go below current stunned"
|
||||
. = STATUS_UPDATE_CANMOVE
|
||||
if((!!amount) == (!!stunned)) // We're not changing from + to 0 or vice versa
|
||||
updating = FALSE
|
||||
. = STATUS_UPDATE_NONE
|
||||
|
||||
if(status_flags & CANSTUN || force)
|
||||
stunned = max(amount, 0)
|
||||
if(updating)
|
||||
update_stat()
|
||||
else
|
||||
return STATUS_UPDATE_NONE
|
||||
|
||||
/mob/living/silicon/robot/SetWeakened(amount, updating = 1, force = 0)
|
||||
. = STATUS_UPDATE_CANMOVE
|
||||
if((!!amount) == (!!weakened)) // We're not changing from + to 0 or vice versa
|
||||
updating = FALSE
|
||||
. = STATUS_UPDATE_NONE
|
||||
if(status_flags & CANWEAKEN || force)
|
||||
weakened = max(amount, 0)
|
||||
if(updating)
|
||||
update_stat()
|
||||
else
|
||||
return STATUS_UPDATE_NONE
|
||||
|
||||
/mob/living/silicon/robot/update_revive(updating = TRUE)
|
||||
. = ..(updating)
|
||||
if(.)
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
#define STAGE_HAUNT 1
|
||||
#define STAGE_SPOOK 2
|
||||
#define STAGE_TORMENT 3
|
||||
#define STAGE_ATTACK 4
|
||||
#define MANIFEST_DELAY 9
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne
|
||||
name = "???"
|
||||
desc = "...."
|
||||
icon = 'icons/goonstation/objects/clothing/mask.dmi'
|
||||
icon_state = "cursedclown"
|
||||
icon_living = "cursedclown"
|
||||
icon_gib = "clown_gib"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
speed = -1
|
||||
attacktext = "attacks"
|
||||
attack_sound = 'sound/items/bikehorn.ogg'
|
||||
del_on_death = TRUE
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB | LETPASSTHROW | PASSGLASS | PASSBLOB//it's practically a ghost when unmanifested (under the floor)
|
||||
loot = list(/obj/item/clothing/mask/cursedclown)
|
||||
a_intent = INTENT_HARM
|
||||
wander = FALSE
|
||||
minimum_distance = 2
|
||||
move_to_delay = 1
|
||||
environment_smash = FALSE
|
||||
pixel_y = 8
|
||||
pressure_resistance = 200
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 1500
|
||||
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)
|
||||
var/mob/living/carbon/human/current_victim
|
||||
var/manifested = FALSE
|
||||
var/switch_stage = 60
|
||||
var/stage = STAGE_HAUNT
|
||||
var/interest = 0
|
||||
var/target_area
|
||||
var/invalid_area_typecache = list(/area/space, /area/centcom)
|
||||
var/eating = FALSE
|
||||
var/smiting = FALSE
|
||||
var/admincluwne = FALSE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/New()
|
||||
. = ..()
|
||||
remove_from_all_data_huds()
|
||||
var/obj/item/card/id/access_card = new (src)
|
||||
access_card.access = get_all_accesses()//THERE IS NO ESCAPE
|
||||
access_card.flags |= NODROP
|
||||
invalid_area_typecache = typecacheof(invalid_area_typecache)
|
||||
Manifest()
|
||||
if(!current_victim)
|
||||
var/turf/current_location = get_turf(src)
|
||||
if(!is_admin_level(current_location.z))//admin spawned off station Z for some reason do not trigger anything else
|
||||
admincluwne = TRUE
|
||||
return
|
||||
Acquire_Victim()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Destroy()
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/attack_hand(mob/living/carbon/human/M)
|
||||
..()
|
||||
playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/CanPass(atom/A, turf/target)
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Life(seconds, times_fired)
|
||||
do_jitter_animation(1000)
|
||||
pixel_y = 8
|
||||
|
||||
if(is_type_in_typecache(get_area(src.loc), invalid_area_typecache))
|
||||
var/area = pick(teleportlocs)
|
||||
var/area/tp = teleportlocs[area]
|
||||
forceMove(pick(get_area_turfs(tp.type)))
|
||||
|
||||
if(!current_victim && !admincluwne)
|
||||
Acquire_Victim()
|
||||
|
||||
if(stage && !manifested)
|
||||
On_Stage()
|
||||
|
||||
if(stage == STAGE_ATTACK)
|
||||
playsound(src, 'sound/spookoween/ghost_whisper.ogg', 75, 1)
|
||||
|
||||
if(eating)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(current_victim)
|
||||
if(prob(5))//checks roughly every 20 ticks
|
||||
if(!admincluwne)
|
||||
if(current_victim.stat == DEAD || current_victim.get_int_organ(/obj/item/organ/internal/honktumor/cursed) || is_type_in_typecache(get_area(T), invalid_area_typecache))
|
||||
Acquire_Victim()
|
||||
|
||||
if(get_dist(src, current_victim) > 9 && !manifested && !is_type_in_typecache(get_area(T), invalid_area_typecache))//if cluwne gets stuck he just teleports
|
||||
do_teleport(src, T)
|
||||
|
||||
interest++
|
||||
if(interest >= switch_stage * 4)
|
||||
stage = STAGE_ATTACK
|
||||
|
||||
else if(interest >= switch_stage * 2)
|
||||
stage = STAGE_TORMENT
|
||||
|
||||
else if(interest >= switch_stage)
|
||||
stage = STAGE_SPOOK
|
||||
|
||||
else if(interest < switch_stage)
|
||||
stage = STAGE_HAUNT
|
||||
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Goto(target, delay, minimum_distance)
|
||||
if(!manifested && !is_type_in_typecache(get_area(current_victim.loc), invalid_area_typecache))
|
||||
walk_to(src, target, minimum_distance, delay)
|
||||
else
|
||||
walk_to(src,0)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/FindTarget()
|
||||
return current_victim
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/CanAttack(atom/the_target)//you will not escape
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/AttackingTarget()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/LoseTarget()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)//prevents runtimes with machine fuckery
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Acquire_Victim(specific)
|
||||
var/list/players_copy = GLOB.player_list.Copy()
|
||||
while(players_copy.len)
|
||||
var/mob/living/carbon/human/H = pick_n_take(players_copy)
|
||||
|
||||
if(specific)
|
||||
H = specific
|
||||
if((!H || H.stat == DEAD) && smiting)//safety check, target somehow DIED after we sent a smite
|
||||
message_admins("Smiting Floor Cluwne was deleted due to a lack of valid target. Someone killed them first, or they ceased to exist.")
|
||||
qdel(src)
|
||||
return
|
||||
if(H.stat != DEAD && !isLivingSSD(H) && H.client && !H.get_int_organ(/obj/item/organ/internal/honktumor/cursed) && !is_type_in_typecache(get_area(H.loc), invalid_area_typecache))
|
||||
current_victim = H
|
||||
return target = current_victim
|
||||
|
||||
if(H && ishuman(H) && H.stat != DEAD && H != current_victim && !isLivingSSD(H) && H.client && !H.get_int_organ(/obj/item/organ/internal/honktumor/cursed) && !is_type_in_typecache(get_area(H.loc), invalid_area_typecache))
|
||||
current_victim = H
|
||||
interest = 0
|
||||
return target = current_victim
|
||||
|
||||
|
||||
message_admins("Floor Cluwne was deleted due to a lack of valid targets, if this was a manually targeted instance please re-evaluate your choice.")
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Manifest()//handles disappearing and appearance anim
|
||||
if(manifested)
|
||||
new /obj/effect/temp_visual/fcluwne_manifest(loc)
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Appear), MANIFEST_DELAY)
|
||||
|
||||
else
|
||||
layer = GAME_PLANE
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
mouse_opacity = 0
|
||||
density = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Appear()//handled in a seperate proc so floor cluwne doesn't appear before the animation finishes
|
||||
layer = MOB_LAYER
|
||||
invisibility = FALSE
|
||||
mouse_opacity = 1
|
||||
density = TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Reset_View(screens, color, mob/living/carbon/human/H)
|
||||
if(screens)
|
||||
for(var/whole_screen in screens)
|
||||
animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING)
|
||||
if(color && H)
|
||||
animate(H.client, color = color, time = 5)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/On_Stage()
|
||||
var/mob/living/carbon/human/H = current_victim
|
||||
switch(stage)
|
||||
|
||||
if(STAGE_HAUNT)
|
||||
|
||||
if(prob(5))
|
||||
H.AdjustEyeBlurry(1)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/spookoween/insane_low_laugh.ogg', 1)
|
||||
|
||||
if(prob(8))
|
||||
H.playsound_local(src,'sound/spookoween/ghost_whisper.ogg', 5)
|
||||
|
||||
if(prob(5))
|
||||
var/obj/item/I = locate() in orange(H, 8)
|
||||
if(I && !I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What threw that?</span>")
|
||||
|
||||
if(STAGE_SPOOK)
|
||||
|
||||
if(prob(4))
|
||||
H.slip("???", 5, 2)
|
||||
to_chat(H, "<span class='warning'>The floor shifts underneath you!</span>")
|
||||
|
||||
if(prob(3))
|
||||
H.playsound_local(src,'sound/spookoween/scary_horn.ogg', 2)
|
||||
|
||||
if(prob(8))
|
||||
H.playsound_local(src,'sound/spookoween/scary_horn2.ogg', 2)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/hallucinations/growl1.ogg', 10)
|
||||
to_chat(H, "<font face='Comic Sans MS'><i>knoh</i></font>")
|
||||
|
||||
if(prob(5))
|
||||
var/obj/item/I = locate() in orange(H, 8)
|
||||
if(I && !I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What threw that?</span>")
|
||||
|
||||
if(prob(4))
|
||||
to_chat(H, "<font face='Comic Sans MS'><i>yalp ot tnaw I</i></font>")
|
||||
Appear()
|
||||
manifested = FALSE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 1)
|
||||
|
||||
if(STAGE_TORMENT)
|
||||
|
||||
if(prob(5))
|
||||
H.slip("???", 5, 2)
|
||||
to_chat(H, "<span class='warning'>The floor shifts underneath you!</span>")
|
||||
|
||||
if(prob(5))
|
||||
playsound(src, pick('sound/spookoween/scary_horn.ogg', 'sound/spookoween/scary_horn2.ogg', 'sound/spookoween/scary_horn3.ogg'), 30, 1)
|
||||
|
||||
if(prob(3))
|
||||
playsound(src, pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg') , 30, 1)
|
||||
|
||||
|
||||
if(prob(4))
|
||||
for(var/obj/item/I in orange(H, 5))
|
||||
if(I && !I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What the hell?!</span>")
|
||||
|
||||
if(prob(5))
|
||||
to_chat(H, "<span class='warning'>Something feels very wrong...</span>")
|
||||
H.playsound_local(src,'sound/hallucinations/behind_you1.ogg', 25)
|
||||
H.flash_eyes()
|
||||
|
||||
if(prob(5))
|
||||
to_chat(H, "<font face='Comic Sans MS'><i>!?REHTOMKNOH eht esiarp uoy oD</i></font>")
|
||||
to_chat(H, "<span class='warning'>Something grabs your foot!</span>")
|
||||
H.playsound_local(src,'sound/hallucinations/i_see_you1.ogg', 25)
|
||||
H.Stun(10)
|
||||
|
||||
if(prob(5))
|
||||
to_chat(H, "<font face='Comic Sans MS'><i>!KNOH ?od nottub siht seod tahW</i></font>")
|
||||
for(var/obj/machinery/M in range(H, 6))
|
||||
M.attack_hand(src)
|
||||
|
||||
if(prob(6))
|
||||
for(var/turf/simulated/floor/O in range(src, 6))
|
||||
O.MakeSlippery(TURF_WET_WATER, 10)
|
||||
playsound(src, 'sound/effects/clownstep1.ogg', 30, 1)
|
||||
|
||||
if(prob(5))
|
||||
to_chat(H, "<span class='userdanger'>WHAT THE FUCK IS THAT?!</span>")
|
||||
to_chat(H, "<font face='Comic Sans MS'><i>.KNOH !nuf hcum os si uoy htiw gniyalP .KNOH KNOH KNOH</i></font>")
|
||||
H.playsound_local(src,'sound/hallucinations/im_here1.ogg', 25)
|
||||
H.reagents.add_reagent("lsd", 3)
|
||||
Appear()
|
||||
manifested = FALSE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 2)
|
||||
for(var/obj/machinery/light/L in range(H, 8))
|
||||
L.flicker()
|
||||
|
||||
if(STAGE_ATTACK)
|
||||
|
||||
if(!eating)
|
||||
for(var/I in getline(src,H))
|
||||
var/turf/T = I
|
||||
for(var/obj/structure/O in T)
|
||||
if(istype(O, /obj/structure/closet))
|
||||
var/obj/structure/closet/locker = O
|
||||
locker.dump_contents()
|
||||
if(O.density || istype(O, /obj/machinery/door/airlock))
|
||||
forceMove(H.loc)
|
||||
if(T.density)
|
||||
forceMove(H.loc)
|
||||
if(H.buckled)
|
||||
H.buckled.unbuckle_mob()
|
||||
manifested = TRUE
|
||||
Manifest()
|
||||
to_chat(H, "<span class='userdanger'>You feel the floor closing in on your feet!</span>")
|
||||
H.Weaken(30)
|
||||
H.emote("scream")
|
||||
H.adjustBruteLoss(10)
|
||||
if(!eating)
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Grab, H), 70)
|
||||
for(var/turf/simulated/floor/O in range(src, 6))
|
||||
O.MakeSlippery(TURF_WET_LUBE, 20)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1)
|
||||
|
||||
eating = TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Grab(mob/living/carbon/human/H)
|
||||
to_chat(H, "<span class='userdanger'>You feel a cold, gloved hand clamp down on your ankle!</span>")
|
||||
for(var/I in 1 to get_dist(src, H))
|
||||
|
||||
if(do_after(src, 10, target = H))
|
||||
step_towards(H, src)
|
||||
playsound(H, pick('sound/effects/bodyscrape-01.ogg', 'sound/effects/bodyscrape-02.ogg'), 20, 1, -4)
|
||||
H.emote("scream")
|
||||
if(prob(25))
|
||||
playsound(src, pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/items/bikehorn.ogg'), 50, 1)
|
||||
|
||||
if(get_dist(src,H) <= 1)
|
||||
visible_message("<span class='danger'>[src] begins dragging [H] under the floor!</span>")
|
||||
|
||||
if(do_after(src, 50, target = H) && eating)
|
||||
H.BecomeBlind()
|
||||
H.layer = GAME_PLANE
|
||||
H.invisibility = INVISIBILITY_MAXIMUM
|
||||
H.mouse_opacity = 0
|
||||
H.density = FALSE
|
||||
H.anchored = TRUE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Kill, H), 100)
|
||||
H.visible_message("<span class='userdanger'>[src] pulls [H] under the floor!</span>")
|
||||
else//some fuck pulled away our food
|
||||
stage = STAGE_TORMENT
|
||||
eating = FALSE
|
||||
|
||||
else//STOP PULLING AWAY THE FOOD
|
||||
eating = FALSE
|
||||
stage = STAGE_TORMENT
|
||||
|
||||
manifested = FALSE
|
||||
Manifest()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Kill(mob/living/carbon/human/H)
|
||||
playsound(H, 'sound/spookoween/scary_horn2.ogg', 100, 0)
|
||||
var/old_color = H.client.color
|
||||
var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0)
|
||||
var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0)
|
||||
H.client.color = pure_red
|
||||
|
||||
animate(H.client, color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT)
|
||||
for(var/turf/T in orange(H, 4))
|
||||
H.add_splatter_floor(T)
|
||||
if(do_after(src, 50, target = H))
|
||||
|
||||
|
||||
if(prob(50) || smiting)
|
||||
H.makeCluwne()
|
||||
|
||||
H.adjustBruteLoss(30)
|
||||
H.adjustBrainLoss(100)
|
||||
for(var/I in H.bodyparts)
|
||||
var/obj/item/organ/external/O = I
|
||||
if(O.name == "head")//irksome runtimes
|
||||
O.droplimb()
|
||||
continue
|
||||
O.drop_organs()
|
||||
O.droplimb()
|
||||
|
||||
Reset_View(FALSE, old_color, H)
|
||||
H.CureBlind()
|
||||
H.layer = initial(H.layer)
|
||||
H.invisibility = initial(H.invisibility)
|
||||
H.mouse_opacity = initial(H.mouse_opacity)
|
||||
H.density = initial(H.density)
|
||||
H.anchored = initial(H.anchored)
|
||||
|
||||
eating = FALSE
|
||||
if(prob(2))
|
||||
switch_stage = switch_stage * 0.75 //he gets a chance to be faster after each feast
|
||||
if(smiting)
|
||||
playsound(loc, 'sound/spookoween/scary_horn2.ogg', 100, 0, -4)
|
||||
qdel(src)
|
||||
else
|
||||
Acquire_Victim()
|
||||
|
||||
interest = 0
|
||||
|
||||
//manifestation animation
|
||||
/obj/effect/temp_visual/fcluwne_manifest
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "fcluwne_manifest"
|
||||
layer = TURF_LAYER
|
||||
duration = 150
|
||||
randomdir = FALSE
|
||||
|
||||
|
||||
/obj/effect/temp_visual/fcluwne_manifest/New()
|
||||
. = ..()
|
||||
playsound(src, 'sound/spookoween/scary_clown_appear.ogg', 100, 1)
|
||||
|
||||
#undef STAGE_HAUNT
|
||||
#undef STAGE_SPOOK
|
||||
#undef STAGE_TORMENT
|
||||
#undef STAGE_ATTACK
|
||||
#undef MANIFEST_DELAY
|
||||
@@ -3,6 +3,10 @@
|
||||
species_allowed = list("Vulpkanin")
|
||||
gender = NEUTER
|
||||
|
||||
/datum/sprite_accessory/facial_hair/vulpkanin/vulp_earfluff
|
||||
name = "Vulpkanin Earfluff"
|
||||
icon_state = "earfluff"
|
||||
|
||||
/datum/sprite_accessory/facial_hair/vulpkanin/vulp_blaze
|
||||
name = "Blaze"
|
||||
icon_state = "blaze"
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
var/centcomm_message_cooldown = 0
|
||||
var/tmp_alertlevel = 0
|
||||
|
||||
var/status_display_freq = "1435"
|
||||
var/stat_msg1
|
||||
var/stat_msg2
|
||||
var/display_type="blank"
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
slots_list += "paranormal: [paranormal_slots]"
|
||||
if(cyborg_slots > 0)
|
||||
slots_list += "cyborg: [cyborg_slots]"
|
||||
var slot_text = list_implode(slots_list, ", ")
|
||||
var/slot_text = jointext(slots_list, ", ")
|
||||
notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]")
|
||||
message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1)
|
||||
log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
post_status(href_list["statdisp"])
|
||||
|
||||
/datum/data/pda/app/status_display/proc/post_status(var/command, var/data1, var/data2)
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(DISPLAY_FREQ)
|
||||
if(!frequency)
|
||||
return
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
|
||||
/obj/item/integrated_radio/signal
|
||||
var/frequency = 1457
|
||||
var/frequency = RSD_FREQ
|
||||
var/code = 30.0
|
||||
var/last_transmission
|
||||
var/datum/radio_frequency/radio_connection
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
/obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P)
|
||||
return {"
|
||||
<ul>
|
||||
<li><b>Frequency:</b> <a href="?src=[UID()];set_freq=-1">[format_frequency(frequency)] GHz</a> (<a href="?src=[UID()];set_freq=[1439]">Reset</a>)</li>
|
||||
<li><b>Frequency:</b> <a href="?src=[UID()];set_freq=-1">[format_frequency(frequency)] GHz</a> (<a href="?src=[UID()];set_freq=[ENGINE_FREQ]">Reset</a>)</li>
|
||||
<li>[format_tag("ID Tag","id_tag","set_id")]</a></li>
|
||||
</ul>
|
||||
"}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
/obj/item/ammo_casing/energy/xray
|
||||
projectile_type = /obj/item/projectile/beam/xray
|
||||
e_cost = 50
|
||||
e_cost = 100
|
||||
fire_sound = 'sound/weapons/laser3.ogg'
|
||||
|
||||
/obj/item/ammo_casing/energy/immolator
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
/obj/item/gun/energy/xray
|
||||
name = "xray laser gun"
|
||||
desc = "A high-power laser gun capable of expelling concentrated xray blasts."
|
||||
desc = "A high-power laser gun capable of expelling concentrated xray blasts. These blasts will penetrate solid objects, but will decrease in power the longer they have to travel."
|
||||
icon_state = "xray"
|
||||
origin_tech = "combat=6;materials=4;magnets=4;syndicate=1"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/xray)
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
animate_movement = 0
|
||||
|
||||
var/damage = 10
|
||||
var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves
|
||||
var/tile_dropoff_s = 0 //same as above but for stamina
|
||||
var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here
|
||||
var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions
|
||||
var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid
|
||||
@@ -60,9 +62,15 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
on_range()
|
||||
range--
|
||||
if(damage && tile_dropoff)
|
||||
damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves
|
||||
if(stamina && tile_dropoff_s)
|
||||
stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina
|
||||
if(range <= 0 && loc)
|
||||
on_range()
|
||||
if(!damage && !stamina && !nodamage)
|
||||
on_range()
|
||||
|
||||
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
|
||||
qdel(src)
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
name = "xray beam"
|
||||
icon_state = "xray"
|
||||
damage = 15
|
||||
tile_dropoff = 0.75
|
||||
irradiate = 30
|
||||
forcedodge = 1
|
||||
range = 15
|
||||
|
||||
@@ -69,17 +69,8 @@
|
||||
/obj/item/projectile/bullet/pellet
|
||||
name = "pellet"
|
||||
damage = 12.5
|
||||
var/tile_dropoff = 0.75
|
||||
var/tile_dropoff_s = 1.25
|
||||
|
||||
/obj/item/projectile/bullet/pellet/Range()
|
||||
..()
|
||||
if(damage > 0)
|
||||
damage -= tile_dropoff
|
||||
if(stamina > 0)
|
||||
stamina -= tile_dropoff_s
|
||||
if(damage < 0 && stamina < 0)
|
||||
qdel(src)
|
||||
tile_dropoff = 0.75
|
||||
tile_dropoff_s = 1.25
|
||||
|
||||
/obj/item/projectile/bullet/pellet/rubber
|
||||
name = "rubber pellet"
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
File: AST Nodes
|
||||
An abstract syntax tree (AST) is a representation of source code in a computer-friendly format. It is composed of nodes,
|
||||
each of which represents a certain part of the source code. For example, an <IfStatement> node represents an if statement in the
|
||||
script's source code. Because it is a representation of the source code in memory, it is independent of any specific scripting language.
|
||||
This allows a script in any language for which a parser exists to be run by the interpreter.
|
||||
|
||||
The AST is produced by an <n_Parser> object. It consists of a <GlobalBlock> with an arbitrary amount of statements. These statements are
|
||||
run in order by an <n_Interpreter> object. A statement may in turn run another block (such as an if statement might if its condition is
|
||||
met).
|
||||
|
||||
Articles:
|
||||
- <http://en.wikipedia.org/wiki/Abstract_syntax_tree>
|
||||
*/
|
||||
/*
|
||||
Constants: Operator Precedence
|
||||
OOP_OR - Logical or
|
||||
OOP_AND - Logical and
|
||||
OOP_BIT - Bitwise operations
|
||||
OOP_EQUAL - Equality checks
|
||||
OOP_COMPARE - Greater than, less then, etc
|
||||
OOP_ADD - Addition and subtraction
|
||||
OOP_MULTIPLY - Multiplication and division
|
||||
OOP_POW - Exponents
|
||||
OOP_UNARY - Unary Operators
|
||||
OOP_GROUP - Parentheses
|
||||
*/
|
||||
/var/const/OOP_OR = 1 //||
|
||||
/var/const/OOP_AND = OOP_OR + 1 //&&
|
||||
/var/const/OOP_BIT = OOP_AND + 1 //&, |
|
||||
/var/const/OOP_EQUAL = OOP_BIT + 1 //==, !=
|
||||
/var/const/OOP_COMPARE = OOP_EQUAL + 1 //>, <, >=, <=
|
||||
/var/const/OOP_ADD = OOP_COMPARE + 1 //+, -
|
||||
/var/const/OOP_MULTIPLY= OOP_ADD + 1 //*, /, %
|
||||
/var/const/OOP_POW = OOP_MULTIPLY + 1 //^
|
||||
/var/const/OOP_UNARY = OOP_POW + 1 //!
|
||||
/var/const/OOP_GROUP = OOP_UNARY + 1 //()
|
||||
|
||||
/*
|
||||
Class: node
|
||||
*/
|
||||
/datum/node/proc/ToString()
|
||||
return "[src.type]"
|
||||
/*
|
||||
Class: identifier
|
||||
*/
|
||||
/datum/node/identifier
|
||||
var/id_name
|
||||
|
||||
/datum/node/identifier/New(id)
|
||||
. = ..()
|
||||
src.id_name = id
|
||||
|
||||
/datum/node/identifier/ToString()
|
||||
return id_name
|
||||
|
||||
/*
|
||||
Class: expression
|
||||
*/
|
||||
/datum/node/expression
|
||||
/*
|
||||
Class: operator
|
||||
See <Binary Operators> and <Unary Operators> for subtypes.
|
||||
*/
|
||||
/datum/node/expression/operator
|
||||
var/datum/node/expression/exp
|
||||
var/token = "" // Used when giving type mismatches.
|
||||
var/tmp/name
|
||||
var/tmp/precedence
|
||||
|
||||
/datum/node/expression/operator/New()
|
||||
.=..()
|
||||
if(!src.name)
|
||||
src.name = "[src.type]"
|
||||
|
||||
/datum/node/expression/operator/ToString()
|
||||
return "operator: [name]"
|
||||
|
||||
/*
|
||||
Class: FunctionCall
|
||||
*/
|
||||
/datum/node/expression/FunctionCall
|
||||
//Function calls can also be expressions or statements.
|
||||
var/func_name
|
||||
var/datum/node/identifier/object
|
||||
var/list/parameters = list()
|
||||
|
||||
/*
|
||||
Class: literal
|
||||
*/
|
||||
/datum/node/expression/value/literal
|
||||
var/value
|
||||
|
||||
/datum/node/expression/value/literal/New(value)
|
||||
. = ..()
|
||||
src.value = value
|
||||
|
||||
/datum/node/expression/value/literal/ToString()
|
||||
return src.value
|
||||
|
||||
/*
|
||||
Class: variable
|
||||
*/
|
||||
/datum/node/expression/value/variable
|
||||
var/datum/node/object //Either a node/identifier or another node/expression/value/variable which points to the object
|
||||
var/datum/node/identifier/id
|
||||
|
||||
|
||||
/datum/node/expression/value/variable/New(ident)
|
||||
. = ..()
|
||||
id = ident
|
||||
if(istext(id))
|
||||
id = new(id)
|
||||
|
||||
/datum/node/expression/value/variable/ToString()
|
||||
return src.id.ToString()
|
||||
|
||||
/*
|
||||
Class: reference
|
||||
*/
|
||||
/datum/node/expression/value/reference
|
||||
var/datum/value
|
||||
|
||||
/datum/node/expression/value/reference/New(value)
|
||||
. = ..()
|
||||
src.value = value
|
||||
|
||||
/datum/node/expression/value/reference/ToString()
|
||||
return "ref: [src.value] ([src.value.type])"
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
File: Block Types
|
||||
*/
|
||||
/*
|
||||
Class: BlockDefinition
|
||||
An object representing a set of actions to perform independently from the rest of the script. Blocks are basically just
|
||||
lists of statements to execute which also contain some local variables and methods. Note that since functions are local to a block,
|
||||
it is possible to have a function definition inside of any type of block (such as in an if statement or another function),
|
||||
and not just in the global scope as in many languages.
|
||||
*/
|
||||
/datum/node/BlockDefinition
|
||||
var/list/statements = new
|
||||
var/list/functions = new
|
||||
var/list/initial_variables = new
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a permanent variable. The variable will not be deleted when it goes out of scope.
|
||||
|
||||
Notes:
|
||||
Since all pre-existing temporary variables are deleted, it is not generally desirable to use this proc after the interpreter has been instantiated.
|
||||
Instead, use <n_Interpreter.SetVar()>.
|
||||
|
||||
See Also:
|
||||
- <n_Interpreter.SetVar()>
|
||||
*/
|
||||
/datum/node/BlockDefinition/proc/SetVar(name, value)
|
||||
initial_variables[name] = value
|
||||
|
||||
|
||||
/*
|
||||
Class: GlobalBlock
|
||||
A block object representing the global scope.
|
||||
*/
|
||||
//
|
||||
/datum/node/BlockDefinition/GlobalBlock/New()
|
||||
initial_variables["null"] = null
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Class: FunctionBlock
|
||||
A block representing a function body.
|
||||
*/
|
||||
//
|
||||
/datum/node/BlockDefinition/FunctionBlock
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
File: Binary Operators
|
||||
*/
|
||||
/*
|
||||
Class: binary
|
||||
Represents a binary operator in the AST. A binary operator takes two operands (ie x and y) and returns a value.
|
||||
*/
|
||||
/datum/node/expression/operator/binary
|
||||
var/datum/node/expression/exp2
|
||||
|
||||
////////// Comparison Operators //////////
|
||||
/*
|
||||
Class: Equal
|
||||
Returns true if x = y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Equal
|
||||
token = "=="
|
||||
precedence = OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: NotEqual
|
||||
Returns true if x and y aren't equal.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/NotEqual
|
||||
token = "!="
|
||||
precedence = OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: Greater
|
||||
Returns true if x > y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Greater
|
||||
token = ">"
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: Less
|
||||
Returns true if x < y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Less
|
||||
token = "<"
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: GreaterOrEqual
|
||||
Returns true if x >= y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/GreaterOrEqual
|
||||
token = ">="
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: LessOrEqual
|
||||
Returns true if x <= y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LessOrEqual
|
||||
token = "<="
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
|
||||
////////// Logical Operators //////////
|
||||
|
||||
/*
|
||||
Class: LogicalAnd
|
||||
Returns true if x and y are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalAnd
|
||||
token = "&&"
|
||||
precedence = OOP_AND
|
||||
|
||||
/*
|
||||
Class: LogicalOr
|
||||
Returns true if x, y, or both are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalOr
|
||||
token = "||"
|
||||
precedence = OOP_OR
|
||||
|
||||
/*
|
||||
Class: LogicalXor
|
||||
Returns true if either x or y but not both are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalXor //Not implemented in nS
|
||||
precedence = OOP_OR
|
||||
|
||||
|
||||
////////// Bitwise Operators //////////
|
||||
|
||||
/*
|
||||
Class: BitwiseAnd
|
||||
Performs a bitwise and operation.
|
||||
|
||||
Example:
|
||||
011 & 110 = 010
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseAnd
|
||||
token = "&"
|
||||
precedence = OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseOr
|
||||
Performs a bitwise or operation.
|
||||
|
||||
Example:
|
||||
011 | 110 = 111
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseOr
|
||||
token = "|"
|
||||
precedence = OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseXor
|
||||
Performs a bitwise exclusive or operation.
|
||||
|
||||
Example:
|
||||
011 xor 110 = 101
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseXor
|
||||
token = "`"
|
||||
precedence = OOP_BIT
|
||||
|
||||
|
||||
////////// Arithmetic Operators //////////
|
||||
|
||||
/*
|
||||
Class: Add
|
||||
Returns the sum of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Add
|
||||
token = "+"
|
||||
precedence = OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Subtract
|
||||
Returns the difference of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Subtract
|
||||
token = "-"
|
||||
precedence = OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Multiply
|
||||
Returns the product of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Multiply
|
||||
token = "*"
|
||||
precedence = OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Divide
|
||||
Returns the quotient of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Divide
|
||||
token = "/"
|
||||
precedence = OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Power
|
||||
Returns x raised to the power of y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Power
|
||||
token = "^"
|
||||
precedence = OOP_POW
|
||||
|
||||
/*
|
||||
Class: Modulo
|
||||
Returns the remainder of x / y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Modulo
|
||||
token = "%"
|
||||
precedence = OOP_MULTIPLY
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
File: Unary Operators
|
||||
*/
|
||||
/*
|
||||
Class: unary
|
||||
Represents a unary operator in the AST. Unary operators take a single operand (referred to as x below) and return a value.
|
||||
*/
|
||||
/datum/node/expression/operator/unary
|
||||
precedence = OOP_UNARY
|
||||
|
||||
/*
|
||||
Class: LogicalNot
|
||||
Returns !x.
|
||||
|
||||
Example:
|
||||
!true = false and !false = true
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/LogicalNot
|
||||
name = "logical not"
|
||||
|
||||
/*
|
||||
Class: BitwiseNot
|
||||
Returns the value of a bitwise not operation performed on x.
|
||||
|
||||
Example:
|
||||
~10 (decimal 2) = 01 (decimal 1).
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/BitwiseNot
|
||||
name = "bitwise not"
|
||||
|
||||
/*
|
||||
Class: Minus
|
||||
Returns -x.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/Minus
|
||||
name = "minus"
|
||||
|
||||
/*
|
||||
Class: group
|
||||
A special unary operator representing a value in parentheses.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/group
|
||||
precedence = OOP_GROUP
|
||||
|
||||
/datum/node/expression/operator/unary/New(var/datum/node/expression/exp)
|
||||
src.exp = exp
|
||||
return ..()
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
File: Statement Types
|
||||
*/
|
||||
/*
|
||||
Class: statement
|
||||
An object representing a single instruction run by an interpreter.
|
||||
*/
|
||||
/datum/node/statement
|
||||
/*
|
||||
Class: FunctionCall
|
||||
Represents a call to a function.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/FunctionCall
|
||||
var/func_name
|
||||
var/datum/node/identifier/object
|
||||
var/list/parameters = new
|
||||
|
||||
/*
|
||||
Class: FunctionDefinition
|
||||
Defines a function.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/FunctionDefinition
|
||||
var/func_name
|
||||
var/list/parameters = new
|
||||
var/datum/node/BlockDefinition/FunctionBlock/block
|
||||
|
||||
/*
|
||||
Class: VariableAssignment
|
||||
Sets a variable in an accessible scope to the given value if one exists, otherwise initializes a new local variable to the given value.
|
||||
|
||||
Notes:
|
||||
If a variable with the same name exists in a higher block, the value will be assigned to it. If not,
|
||||
a new variable is created in the current block. To force creation of a new variable, use <VariableDeclaration>.
|
||||
|
||||
See Also:
|
||||
- <VariableDeclaration>
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/VariableAssignment
|
||||
var/datum/node/identifier/object
|
||||
var/datum/node/identifier/var_name
|
||||
var/datum/node/expression/value
|
||||
|
||||
/*
|
||||
Class: VariableDeclaration
|
||||
Intializes a local variable to a null value.
|
||||
|
||||
See Also:
|
||||
- <VariableAssignment>
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/VariableDeclaration
|
||||
var/datum/node/identifier/object
|
||||
var/datum/node/identifier/var_name
|
||||
|
||||
/*
|
||||
Class: IfStatement
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/IfStatement
|
||||
var/skip = 0
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/BlockDefinition/else_block //may be null
|
||||
var/datum/node/expression/cond
|
||||
var/datum/node/statement/else_if
|
||||
|
||||
/datum/node/statement/IfStatement/ElseIf
|
||||
|
||||
/*
|
||||
Class: WhileLoop
|
||||
Loops while a given condition is true.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/WhileLoop
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/expression/cond
|
||||
|
||||
/*
|
||||
Class: ForLoop
|
||||
Loops while test is true, initializing a variable, increasing the variable
|
||||
*/
|
||||
/datum/node/statement/ForLoop
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/expression/test
|
||||
var/datum/node/expression/init
|
||||
var/datum/node/expression/increment
|
||||
|
||||
/*
|
||||
Class: BreakStatement
|
||||
Ends a loop.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/BreakStatement
|
||||
|
||||
/*
|
||||
Class: ContinueStatement
|
||||
Skips to the next iteration of a loop.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/ContinueStatement
|
||||
|
||||
/*
|
||||
Class: ReturnStatement
|
||||
Ends the function and returns a value.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/ReturnStatement
|
||||
var/datum/node/expression/value
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
File: Errors
|
||||
*/
|
||||
/*
|
||||
Class: scriptError
|
||||
An error scanning or parsing the source code.
|
||||
*/
|
||||
/datum/scriptError
|
||||
/*
|
||||
Var: message
|
||||
A message describing the problem.
|
||||
*/
|
||||
var/message
|
||||
|
||||
/datum/scriptError/New(msg = null)
|
||||
if(msg)message = msg
|
||||
|
||||
/datum/scriptError/BadToken
|
||||
message = "Unexpected token: "
|
||||
var/datum/token/token
|
||||
|
||||
/datum/scriptError/BadToken/New(datum/token/t)
|
||||
token = t
|
||||
if(t && t.line)
|
||||
message = "[t.line]: [message]"
|
||||
|
||||
if(istype(t))
|
||||
message += "[t.value]"
|
||||
|
||||
else
|
||||
message += "[t]"
|
||||
|
||||
/datum/scriptError/InvalidID
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Invalid identifier name: "
|
||||
|
||||
/datum/scriptError/ReservedWord
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Identifer using reserved word: "
|
||||
|
||||
/datum/scriptError/BadNumber
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Bad number: "
|
||||
|
||||
/datum/scriptError/BadReturn
|
||||
var/datum/token/token
|
||||
message = "Unexpected return statement outside of a function."
|
||||
|
||||
/datum/scriptError/BadReturn/New(datum/token/t)
|
||||
src.token = t
|
||||
|
||||
/datum/scriptError/EndOfFile
|
||||
message = "Unexpected end of file."
|
||||
|
||||
/datum/scriptError/ExpectedToken
|
||||
message = "Expected: '"
|
||||
|
||||
/datum/scriptError/ExpectedToken/New(id, datum/token/T)
|
||||
if(T && T.line)
|
||||
message = "[T.line]: [message]"
|
||||
|
||||
message += "[id]'. "
|
||||
|
||||
if(T)
|
||||
message += "Found '[T.value]'."
|
||||
|
||||
|
||||
/datum/scriptError/UnterminatedComment
|
||||
message = "Unterminated multi-line comment statement: expected */"
|
||||
|
||||
/datum/scriptError/DuplicateFunction/New(name, datum/token/t)
|
||||
message = "Function '[name]' defined twice."
|
||||
|
||||
/datum/scriptError/ParameterFunction
|
||||
message = "You cannot use a function inside a parameter."
|
||||
|
||||
/datum/scriptError/ParameterFunction/New(datum/token/t)
|
||||
var/line = "?"
|
||||
if(t)
|
||||
line = t.line
|
||||
message = "[line]: [message]"
|
||||
|
||||
/*
|
||||
Class: runtimeError
|
||||
An error thrown by the interpreter in running the script.
|
||||
*/
|
||||
/datum/runtimeError
|
||||
var/name
|
||||
/*
|
||||
Var: message
|
||||
A basic description as to what went wrong.
|
||||
*/
|
||||
var/message
|
||||
var/datum/stack/stack
|
||||
/*
|
||||
Proc: ToString
|
||||
Returns a description of the error suitable for showing to the user.
|
||||
*/
|
||||
/datum/runtimeError/proc/ToString()
|
||||
. = "[name]: [message]"
|
||||
if(!stack.Top())
|
||||
return
|
||||
|
||||
. += "\nStack:"
|
||||
while(stack.Top())
|
||||
var/datum/node/statement/FunctionCall/stmt = stack.Pop()
|
||||
. += "\n\t [stmt.func_name]()"
|
||||
|
||||
/datum/runtimeError/TypeMismatch
|
||||
name = "TypeMismatchError"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/unary/New(op, a)
|
||||
message = "Type mismatch: [op]'[a]'"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||
|
||||
/datum/runtimeError/UnexpectedReturn
|
||||
name = "UnexpectedReturnError"
|
||||
message = "Unexpected return statement."
|
||||
|
||||
/datum/runtimeError/UnknownInstruction
|
||||
name = "UnknownInstructionError"
|
||||
message = "Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
||||
|
||||
/datum/runtimeError/UndefinedVariable
|
||||
name = "UndefinedVariableError"
|
||||
|
||||
/datum/runtimeError/UndefinedVariable/New(variable)
|
||||
message = "Variable '[variable]' has not been declared."
|
||||
|
||||
/datum/runtimeError/UndefinedFunction
|
||||
name = "UndefinedFunctionError"
|
||||
|
||||
/datum/runtimeError/UndefinedFunction/New(function)
|
||||
message = "Function '[function]()' has not been defined."
|
||||
|
||||
/datum/runtimeError/DuplicateVariableDeclaration
|
||||
name = "DuplicateVariableError"
|
||||
|
||||
/datum/runtimeError/DuplicateVariableDeclaration/New(variable)
|
||||
message="Variable '[variable]' was already declared."
|
||||
|
||||
/datum/runtimeError/IterationLimitReached
|
||||
name = "MaxIterationError"
|
||||
message = "A loop has reached its maximum number of iterations."
|
||||
|
||||
/datum/runtimeError/RecursionLimitReached
|
||||
name = "MaxRecursionError"
|
||||
message = "The maximum amount of recursion has been reached."
|
||||
|
||||
/datum/runtimeError/DivisionByZero
|
||||
name = "DivideByZeroError"
|
||||
message = "Division by zero attempted."
|
||||
|
||||
/datum/runtimeError/MaxCPU
|
||||
name = "MaxComputationalUse"
|
||||
message = "Maximum amount of computational cycles reached (>= 1000)."
|
||||
|
||||
/datum/runtimeError/VectorLimit
|
||||
name = "VectorSizeOverflow"
|
||||
message = "Maximum vector size reached"
|
||||
|
||||
/datum/runtimeError/StringLimit
|
||||
name = "StringSizeOverflow"
|
||||
message = "Maximum string size reached"
|
||||
@@ -1,352 +0,0 @@
|
||||
/* --- Traffic Control Scripting Language --- */
|
||||
// Nanotrasen TCS Language - Made by Doohl
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter
|
||||
var/datum/TCS_Compiler/Compiler
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter/HandleError(datum/runtimeError/e)
|
||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter/GC()
|
||||
..()
|
||||
Compiler = null
|
||||
|
||||
/datum/TCS_Compiler
|
||||
var/datum/n_Interpreter/TCS_Interpreter/interpreter
|
||||
var/obj/machinery/telecomms/server/Holder // the server that is running the code
|
||||
var/ready = 1 // 1 if ready to run code
|
||||
|
||||
/* -- Set ourselves to Garbage Collect -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/GC()
|
||||
Holder = null
|
||||
if(interpreter)
|
||||
interpreter.GC()
|
||||
|
||||
|
||||
//temp
|
||||
/datum/TCS_Compiler
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
var/datum/n_Scanner/nS_Scanner/scanner
|
||||
var/list/tokens
|
||||
var/datum/n_Parser/nS_Parser/parser
|
||||
var/datum/node/BlockDefinition/GlobalBlock/program
|
||||
|
||||
/* -- Compile a raw block of text -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/Compile(list/code)
|
||||
options = new()
|
||||
scanner = new(code, options)
|
||||
tokens = scanner.Scan()
|
||||
parser = new(tokens, options)
|
||||
program = parser.Parse()
|
||||
|
||||
var/list/returnerrors = list()
|
||||
|
||||
returnerrors += scanner.errors
|
||||
returnerrors += parser.errors
|
||||
|
||||
if(returnerrors.len)
|
||||
return returnerrors
|
||||
|
||||
interpreter = new(program)
|
||||
interpreter.persist = 1
|
||||
interpreter.Compiler= src
|
||||
|
||||
return returnerrors
|
||||
|
||||
/* -- Execute the compiled code -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/Run(var/datum/signal/signal)
|
||||
if(!ready)
|
||||
return
|
||||
|
||||
if(!interpreter)
|
||||
return
|
||||
|
||||
interpreter.container = src
|
||||
|
||||
interpreter.CreateGlobalScope() // Reset the variables.
|
||||
interpreter.curScope = interpreter.globalScope
|
||||
|
||||
interpreter.SetVar("PI", 3.141592653) // value of pi
|
||||
interpreter.SetVar("E", 2.718281828) // value of e
|
||||
interpreter.SetVar("SQURT2", 1.414213562) // value of the square root of 2
|
||||
interpreter.SetVar("FALSE", 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("false", 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("TRUE", 1) // boolean shortcut to 1
|
||||
interpreter.SetVar("true", 1) // boolean shortcut to 1
|
||||
|
||||
interpreter.SetVar("NORTH", NORTH) // NORTH (1)
|
||||
interpreter.SetVar("SOUTH", SOUTH) // SOUTH (2)
|
||||
interpreter.SetVar("EAST", EAST) // EAST (4)
|
||||
interpreter.SetVar("WEST", WEST) // WEST (8)
|
||||
|
||||
// Channel macros
|
||||
interpreter.SetVar("$common", PUB_FREQ)
|
||||
interpreter.SetVar("$science", SCI_FREQ)
|
||||
interpreter.SetVar("$command", COMM_FREQ)
|
||||
interpreter.SetVar("$medical", MED_FREQ)
|
||||
interpreter.SetVar("$engineering", ENG_FREQ)
|
||||
interpreter.SetVar("$security", SEC_FREQ)
|
||||
interpreter.SetVar("$supply", SUP_FREQ)
|
||||
interpreter.SetVar("$service", SRV_FREQ)
|
||||
|
||||
// Signal data
|
||||
|
||||
interpreter.SetVar("$content", signal.data["message"])
|
||||
interpreter.SetVar("$freq", signal.frequency)
|
||||
interpreter.SetVar("$source", signal.data["name"])
|
||||
interpreter.SetVar("$job", signal.data["job"])
|
||||
interpreter.SetVar("$sign", signal)
|
||||
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
||||
|
||||
var/datum/language/speaking = signal.data["language"]
|
||||
if(speaking)
|
||||
interpreter.SetVar("$language", speaking.name)
|
||||
else
|
||||
interpreter.SetVar("$language", "Unknown")
|
||||
|
||||
// Set up the script procs
|
||||
|
||||
/*
|
||||
-> Send another signal to a server
|
||||
@format: broadcast(content, frequency, source, job)
|
||||
|
||||
@param content: Message to broadcast
|
||||
@param frequency: Frequency to broadcast to
|
||||
@param source: The name of the source you wish to imitate. Must be stored in stored_names list.
|
||||
@param job: The name of the job.
|
||||
*/
|
||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||
|
||||
/*
|
||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||
@format: mem(address, value)
|
||||
|
||||
@param address: The memory address (string index) to store a value to
|
||||
@param value: The value to store to the memory address
|
||||
*/
|
||||
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
||||
|
||||
/*
|
||||
-> Delay code for a given amount of deciseconds
|
||||
@format: sleep(time)
|
||||
|
||||
@param time: time to sleep in deciseconds (1/10th second)
|
||||
*/
|
||||
interpreter.SetProc("sleep", "delay", signal, list("time"))
|
||||
|
||||
/*
|
||||
-> Replaces a string with another string
|
||||
@format: replace(string, substring, replacestring)
|
||||
|
||||
@param string: the string to search for substrings (best used with $content$ constant)
|
||||
@param substring: the substring to search for
|
||||
@param replacestring: the string to replace the substring with
|
||||
|
||||
*/
|
||||
interpreter.SetProc("replace", /proc/n_replacetext)
|
||||
|
||||
/*
|
||||
-> Locates an element/substring inside of a list or string
|
||||
@format: find(haystack, needle, start = 1, end = 0)
|
||||
|
||||
@param haystack: the container to search
|
||||
@param needle: the element to search for
|
||||
@param start: the position to start in
|
||||
@param end: the position to end in
|
||||
|
||||
*/
|
||||
interpreter.SetProc("find", /proc/smartfind)
|
||||
|
||||
/*
|
||||
-> Finds the length of a string or list
|
||||
@format: length(container)
|
||||
|
||||
@param container: the list or container to measure
|
||||
|
||||
*/
|
||||
interpreter.SetProc("length", /proc/smartlength)
|
||||
|
||||
/* -- Clone functions, carried from default BYOND procs --- */
|
||||
|
||||
// vector namespace
|
||||
interpreter.SetProc("vector", /proc/n_list)
|
||||
interpreter.SetProc("at", /proc/n_listpos)
|
||||
interpreter.SetProc("copy", /proc/n_listcopy)
|
||||
interpreter.SetProc("push_back", /proc/n_listadd)
|
||||
interpreter.SetProc("remove", /proc/n_listremove)
|
||||
interpreter.SetProc("cut", /proc/n_listcut)
|
||||
interpreter.SetProc("swap", /proc/n_listswap)
|
||||
interpreter.SetProc("insert", /proc/n_listinsert)
|
||||
|
||||
interpreter.SetProc("pick", /proc/n_pick)
|
||||
interpreter.SetProc("prob", /proc/prob_chance)
|
||||
interpreter.SetProc("substr", /proc/docopytext)
|
||||
|
||||
interpreter.SetProc("shuffle", /proc/shuffle)
|
||||
interpreter.SetProc("uniquevector", /proc/uniquelist)
|
||||
|
||||
interpreter.SetProc("text2vector", /proc/n_splittext)
|
||||
interpreter.SetProc("vector2text", /proc/n_jointext)
|
||||
|
||||
// Donkie~
|
||||
// Strings
|
||||
interpreter.SetProc("lower", /proc/n_lower)
|
||||
interpreter.SetProc("upper", /proc/n_upper)
|
||||
interpreter.SetProc("explode", /proc/string_explode)
|
||||
interpreter.SetProc("implode", /proc/n_jointext)
|
||||
interpreter.SetProc("repeat", /proc/n_repeat)
|
||||
interpreter.SetProc("reverse", /proc/reverse_text)
|
||||
interpreter.SetProc("tonum", /proc/n_str2num)
|
||||
interpreter.SetProc("capitalize", /proc/capitalize)
|
||||
interpreter.SetProc("replacetextEx",/proc/n_replacetextEx)
|
||||
|
||||
// Numbers
|
||||
interpreter.SetProc("tostring", /proc/n_num2str)
|
||||
interpreter.SetProc("sqrt", /proc/n_sqrt)
|
||||
interpreter.SetProc("abs", /proc/n_abs)
|
||||
interpreter.SetProc("floor", /proc/n_floor)
|
||||
interpreter.SetProc("ceil", /proc/n_ceiling)
|
||||
interpreter.SetProc("round", /proc/n_round)
|
||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||
interpreter.SetProc("inrange", /proc/n_isInRange)
|
||||
interpreter.SetProc("rand", /proc/rand_chance)
|
||||
interpreter.SetProc("arctan", /proc/Atan2)
|
||||
interpreter.SetProc("lcm", /proc/n_lcm)
|
||||
interpreter.SetProc("gcd", /proc/Gcd)
|
||||
interpreter.SetProc("mean", /proc/Mean)
|
||||
interpreter.SetProc("root", /proc/n_root)
|
||||
interpreter.SetProc("sin", /proc/n_sin)
|
||||
interpreter.SetProc("cos", /proc/n_cos)
|
||||
interpreter.SetProc("arcsin", /proc/n_asin)
|
||||
interpreter.SetProc("arccos", /proc/n_acos)
|
||||
interpreter.SetProc("tan", /proc/n_tan)
|
||||
interpreter.SetProc("csc", /proc/n_csc)
|
||||
interpreter.SetProc("cot", /proc/n_cot)
|
||||
interpreter.SetProc("sec", /proc/n_sec)
|
||||
interpreter.SetProc("todegrees", /proc/n_toDegrees)
|
||||
interpreter.SetProc("toradians", /proc/n_toRadians)
|
||||
interpreter.SetProc("lerp", /proc/Lerp)
|
||||
interpreter.SetProc("max", /proc/n_max)
|
||||
interpreter.SetProc("min", /proc/n_min)
|
||||
|
||||
// End of Donkie~
|
||||
|
||||
// Time
|
||||
interpreter.SetProc("time", /proc/time)
|
||||
interpreter.SetProc("timestamp", /proc/gameTimestamp)
|
||||
|
||||
// Run the compiled code
|
||||
interpreter.Run()
|
||||
|
||||
// Backwards-apply variables onto signal data
|
||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||
|
||||
/* Okay, so, the original 'sanitizing' code... did fucking nothing. Then PJB fixed it, which means no HTML.
|
||||
But I like HTML, so back to no sanitizing.*/
|
||||
|
||||
var/message = interpreter.GetVar("$content")
|
||||
var/regex/bannedTags = new ("(<script|<iframe|<video|<audio|<embed|<svg)", "i")
|
||||
if(bannedTags.Find(message)) //uh oh
|
||||
message_admins("Warning: Current Telecomms script contains banned html. Stripping message.")
|
||||
log_admin("Warning: Current Telecomms script contains banned html. Stripping message.")
|
||||
message = interpreter.GetCleanVar("$content", signal.data["message"])
|
||||
|
||||
signal.data["message"] = message
|
||||
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
|
||||
|
||||
var/setname = interpreter.GetVar("$source", signal.data["name"])
|
||||
|
||||
if(signal.data["name"] != setname)
|
||||
signal.data["realname"] = setname
|
||||
signal.data["name"] = setname
|
||||
signal.data["job"] = interpreter.GetVar("$job", signal.data["job"])
|
||||
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
|
||||
|
||||
// If the message is invalid, just don't broadcast it!
|
||||
if(signal.data["message"] == "" || !signal.data["message"])
|
||||
signal.data["reject"] = 1
|
||||
|
||||
/* -- Actual language proc code -- */
|
||||
/datum/signal/proc/delay(var/time)
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/datum/n_Interpreter/TCS_Interpreter/interpreter = S.Compiler.interpreter
|
||||
// Backup the scope
|
||||
var/datum/scope/globalScope = interpreter.globalScope
|
||||
var/datum/scope/curScope = interpreter.curScope
|
||||
var/list/scopes = interpreter.scopes.stack.Copy()
|
||||
var/list/functions = interpreter.functions.stack.Copy()
|
||||
var/datum/node/statement/FunctionDefinition/curFunction = interpreter.curFunction
|
||||
var/status = interpreter.status
|
||||
var/returnVal = interpreter.returnVal
|
||||
// Sleep
|
||||
sleep(time)
|
||||
// Restore the scope
|
||||
interpreter.returnVal = returnVal
|
||||
interpreter.status = status
|
||||
interpreter.curFunction = curFunction
|
||||
interpreter.functions.stack = functions
|
||||
interpreter.scopes.stack = scopes
|
||||
interpreter.curScope = curScope
|
||||
interpreter.globalScope = globalScope
|
||||
|
||||
/datum/signal/proc/mem(var/address, var/value)
|
||||
if(istext(address))
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
|
||||
if(!value && value != 0)
|
||||
return S.memory[address]
|
||||
|
||||
else
|
||||
S.memory[address] = value
|
||||
|
||||
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||
var/datum/signal/newsign = new
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/obj/item/radio/hradio = S.server_radio
|
||||
|
||||
if(!hradio)
|
||||
error("[src] has no radio.")
|
||||
return
|
||||
|
||||
if((!message || message == "") && message != 0)
|
||||
message = "*beep*"
|
||||
if(!source)
|
||||
source = "[html_encode(uppertext(S.id))]"
|
||||
hradio = new // sets the hradio as a radio intercom
|
||||
if(!freq || (!isnum(freq) && text2num(freq) == null))
|
||||
freq = 1459
|
||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||
freq *= 10 // shift the decimal one place
|
||||
|
||||
if(!job)
|
||||
job = "?"
|
||||
|
||||
newsign.data["mob"] = null
|
||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||
newsign.data["race"] = "Automated Signal"
|
||||
newsign.data["name"] = source
|
||||
newsign.data["realname"] = newsign.data["name"]
|
||||
newsign.data["job"] = "[job]"
|
||||
newsign.data["compression"] = 0
|
||||
newsign.data["message"] = message
|
||||
newsign.data["type"] = 2 // artificial broadcast
|
||||
if(!isnum(freq))
|
||||
freq = text2num(freq)
|
||||
newsign.frequency = freq
|
||||
|
||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||
newsign.data["connection"] = connection
|
||||
|
||||
|
||||
newsign.data["radio"] = hradio
|
||||
newsign.data["vmessage"] = message
|
||||
newsign.data["vname"] = source
|
||||
newsign.data["vmask"] = 0
|
||||
newsign.data["level"] = data["level"]
|
||||
|
||||
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
|
||||
if(!pass)
|
||||
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
|
||||
@@ -1,305 +0,0 @@
|
||||
// Script -> BYOND code procs
|
||||
#define SCRIPT_MAX_REPLACEMENTS_ALLOWED 200
|
||||
// --- List operations (lists known as vectors in n_script) ---
|
||||
|
||||
// Clone of list()
|
||||
/proc/n_list()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_list() called tick#: [world.time]")
|
||||
var/list/returnlist = list()
|
||||
for(var/e in args)
|
||||
returnlist.Add(e)
|
||||
return returnlist
|
||||
|
||||
// Clone of pick()
|
||||
/proc/n_pick()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_pick() called tick#: [world.time]")
|
||||
var/list/finalpick = list()
|
||||
for(var/e in args)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
var/list/sublist = e
|
||||
for(var/sube in sublist)
|
||||
finalpick.Add(sube)
|
||||
continue
|
||||
finalpick.Add(e)
|
||||
|
||||
return pick(finalpick)
|
||||
|
||||
// Clone of list[]
|
||||
/proc/n_listpos(var/list/L, var/pos, var/value)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listpos() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
if(isnum(pos))
|
||||
if(!value)
|
||||
if(L.len >= pos && !(pos > L.len))
|
||||
return L[pos]
|
||||
else
|
||||
if(L.len >= pos && !(pos > L.len))
|
||||
L[pos] = value
|
||||
else if(istext(pos))
|
||||
if(!value)
|
||||
return L[pos]
|
||||
else
|
||||
L[pos] = value
|
||||
|
||||
// Clone of list.Copy()
|
||||
/proc/n_listcopy(var/list/L, var/start, var/end)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listcopy() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Copy(start, end)
|
||||
|
||||
// Clone of list.Add()
|
||||
/proc/n_listadd()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listadd() called tick#: [world.time]")
|
||||
var/list/chosenlist
|
||||
var/i = 1
|
||||
for(var/e in args)
|
||||
if(i == 1)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
chosenlist = e
|
||||
i = 2
|
||||
else
|
||||
if(chosenlist)
|
||||
chosenlist.Add(e)
|
||||
|
||||
// Clone of list.Remove()
|
||||
/proc/n_listremove()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listremove() called tick#: [world.time]")
|
||||
var/list/chosenlist
|
||||
var/i = 1
|
||||
for(var/e in args)
|
||||
if(i == 1)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
chosenlist = e
|
||||
i = 2
|
||||
else
|
||||
if(chosenlist)
|
||||
chosenlist.Remove(e)
|
||||
|
||||
// Clone of list.len = 0
|
||||
/proc/n_listcut(var/list/L, var/start, var/end)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listcut() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Cut(start, end)
|
||||
|
||||
// Clone of list.Swap()
|
||||
/proc/n_listswap(var/list/L, var/firstindex, var/secondindex)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listswap() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
if(L.len >= secondindex && L.len >= firstindex)
|
||||
return L.Swap(firstindex, secondindex)
|
||||
|
||||
// Clone of list.Insert()
|
||||
/proc/n_listinsert(var/list/L, var/index, var/element)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listinsert() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Insert(index, element)
|
||||
|
||||
// --- Miscellaneous functions ---
|
||||
|
||||
// Clone of rand()
|
||||
/proc/rand_chance(var/low = 0, var/high)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/rand_chance() called tick#: [world.time]")
|
||||
return rand(low, high)
|
||||
|
||||
// Clone of prob()
|
||||
/proc/prob_chance(var/chance)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/prob_chance() called tick#: [world.time]")
|
||||
return prob(chance)
|
||||
|
||||
// Merge of list.Find() and findtext()
|
||||
/proc/smartfind(var/haystack, var/needle, var/start = 1, var/end = 0)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/smartfind() called tick#: [world.time]")
|
||||
if(haystack && needle)
|
||||
if(isobject(haystack))
|
||||
if(istype(haystack, /list))
|
||||
if(length(haystack) >= end && start > 0)
|
||||
var/list/listhaystack = haystack
|
||||
return listhaystack.Find(needle, start, end)
|
||||
|
||||
else
|
||||
if(istext(haystack))
|
||||
if(length(haystack) >= end && start > 0)
|
||||
return findtext(haystack, needle, start, end)
|
||||
|
||||
// Clone of copytext()
|
||||
/proc/docopytext(var/string, var/start = 1, var/end = 0)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/docopytext() called tick#: [world.time]")
|
||||
if(istext(string) && isnum(start) && isnum(end))
|
||||
if(start > 0)
|
||||
return copytext(string, start, end)
|
||||
|
||||
// Clone of length()
|
||||
/proc/smartlength(var/container)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/smartlength() called tick#: [world.time]")
|
||||
if(container)
|
||||
if(istype(container, /list) || istext(container))
|
||||
return length(container)
|
||||
return 0
|
||||
|
||||
// BY DONKIE~
|
||||
// String stuff
|
||||
/proc/n_lower(var/string)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_lower() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return lowertext(string)
|
||||
|
||||
/proc/n_upper(var/string)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_upper() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return uppertext(string)
|
||||
|
||||
/proc/time()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/time() called tick#: [world.time]")
|
||||
return world.time + (12 HOURS)
|
||||
|
||||
proc/string_explode(var/string, var/separator = "")
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/string_explode() called tick#: [world.time]")
|
||||
if(istext(string) && (istext(separator) || isnull(separator)))
|
||||
return splittext(string, separator)
|
||||
|
||||
//Converts a list to a string
|
||||
/proc/list_implode(var/list/li, var/separator)
|
||||
if(istype(li) && (istext(separator) || isnull(separator)))
|
||||
return jointext(li, separator)
|
||||
|
||||
proc/n_repeat(var/string, var/amount)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_repeat() called tick#: [world.time]")
|
||||
if(istext(string) && isnum(amount))
|
||||
var/i
|
||||
var/newstring = ""
|
||||
if(length(newstring)*amount >=1000)
|
||||
return
|
||||
for(i=0, i<=amount, i++)
|
||||
if(i>=1000)
|
||||
break
|
||||
newstring = newstring + string
|
||||
|
||||
return newstring
|
||||
|
||||
// I don't know if it's neccesary to make my own proc, but I think I have to to be able to check for istext.
|
||||
proc/n_str2num(var/string)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_str2num() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return text2num(string)
|
||||
|
||||
// Clamps N between min and max
|
||||
/proc/n_clamp(var/num, var/min = 0, var/max = 1)
|
||||
if(isnum(num) && isnum(min) && isnum(max))
|
||||
return Clamp(num, min, max)
|
||||
|
||||
// Number shit
|
||||
proc/n_num2str(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_num2str() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return num2text(num)
|
||||
|
||||
// Squareroot
|
||||
proc/n_sqrt(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_sqrt() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return sqrt(num)
|
||||
|
||||
// Magnitude of num
|
||||
proc/n_abs(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_abs() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return abs(num)
|
||||
|
||||
// Round down
|
||||
proc/n_floor(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_floor() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return Floor(num)
|
||||
|
||||
// Round up
|
||||
proc/n_ceiling(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_ceil() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return Ceiling(num)
|
||||
|
||||
// Round to nearest integer
|
||||
proc/n_round(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_round() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
if(num-round(num)<0.5)
|
||||
return round(num)
|
||||
return Ceiling(num)
|
||||
|
||||
// END OF BY DONKIE :(
|
||||
|
||||
/proc/n_sin(var/const/x)
|
||||
if(isnum(x))
|
||||
return sin(x)
|
||||
|
||||
/proc/n_cos(var/const/x)
|
||||
if(isnum(x))
|
||||
return cos(x)
|
||||
|
||||
/proc/n_tan(var/const/x)
|
||||
if(isnum(x))
|
||||
return Tan(x)
|
||||
|
||||
/proc/n_csc(var/const/x)
|
||||
if(isnum(x))
|
||||
return Csc(x)
|
||||
|
||||
/proc/n_cot(var/const/x)
|
||||
if(isnum(x))
|
||||
return Cot(x)
|
||||
|
||||
/proc/n_sec(var/const/x)
|
||||
if(isnum(x))
|
||||
return Sec(x)
|
||||
|
||||
/proc/n_asin(var/const/x)
|
||||
if(isnum(x))
|
||||
return arcsin(x)
|
||||
|
||||
/proc/n_acos(var/const/x)
|
||||
if(isnum(x))
|
||||
return arccos(x)
|
||||
|
||||
/proc/n_isInRange(var/const/x, var/const/min, var/const/max)
|
||||
if(isnum(x) && isnum(min) && isnum(max))
|
||||
return IsInRange(x, min, max)
|
||||
|
||||
/proc/n_lcm(var/const/a, var/const/b)
|
||||
if(isnum(a) && isnum(b))
|
||||
return Lcm(a, b)
|
||||
|
||||
/proc/n_root(var/const/n, var/const/x)
|
||||
if(isnum(n) && isnum(x))
|
||||
return Root(n, x)
|
||||
|
||||
/proc/n_toDegrees(var/const/x)
|
||||
if(isnum(x))
|
||||
return ToDegrees(x)
|
||||
|
||||
/proc/n_toRadians(var/const/x)
|
||||
if(isnum(x))
|
||||
return ToRadians(x)
|
||||
|
||||
/proc/n_max(...)
|
||||
return max(arglist(args))
|
||||
|
||||
/proc/n_min(...)
|
||||
return min(arglist(args))
|
||||
|
||||
/proc/n_log(var/num)
|
||||
if(isnum(num) && 0 < num)
|
||||
return log(num)
|
||||
|
||||
/proc/n_replacetext(text, r, with)
|
||||
return replacetext(text, r, with)
|
||||
|
||||
/proc/n_replacetextEx(text, r, with)
|
||||
return replacetextEx(text, r, with)
|
||||
|
||||
/proc/n_jointext(list, glue)
|
||||
return jointext(list, glue)
|
||||
|
||||
/proc/n_splittext(text, delim)
|
||||
return splittext(text, delim)
|
||||
@@ -1,203 +0,0 @@
|
||||
/proc/isobject(x)
|
||||
return (istype(x, /datum) || istype(x, /list) || istype(x, /savefile) || istype(x, /client) || (x == world))
|
||||
|
||||
/datum/n_Interpreter/proc/Eval(datum/node/expression/exp)
|
||||
if(istype(exp, /datum/node/expression/FunctionCall))
|
||||
return RunFunction(exp)
|
||||
|
||||
else if(istype(exp, /datum/node/expression/operator))
|
||||
return EvalOperator(exp)
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/literal))
|
||||
var/datum/node/expression/value/literal/lit = exp
|
||||
return lit.value
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/reference))
|
||||
var/datum/node/expression/value/reference/ref = exp
|
||||
return ref.value
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/variable))
|
||||
var/datum/node/expression/value/variable/v = exp
|
||||
if(!v.object)
|
||||
return Eval(GetVariable(v.id.id_name))
|
||||
else
|
||||
var/datum/D
|
||||
if(istype(v.object, /datum/node/identifier))
|
||||
D = GetVariable(v.object:id_name)
|
||||
else
|
||||
D = v.object
|
||||
|
||||
D = Eval(D)
|
||||
if(!isobject(D))
|
||||
return null
|
||||
|
||||
if(!D.vars.Find(v.id.id_name))
|
||||
RaiseError(new/datum/runtimeError/UndefinedVariable("[v.object.ToString()].[v.id.id_name]"))
|
||||
return null
|
||||
|
||||
return Eval(D.vars[v.id.id_name])
|
||||
|
||||
else if(istype(exp, /datum/node/expression))
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
else
|
||||
return exp
|
||||
|
||||
/datum/n_Interpreter/proc/EvalOperator(datum/node/expression/operator/exp)
|
||||
if(istype(exp, /datum/node/expression/operator/binary))
|
||||
var/datum/node/expression/operator/binary/bin = exp
|
||||
try // This way we can forgo sanity in the actual evaluation (other than divide by 0).
|
||||
switch(bin.type)
|
||||
if(/datum/node/expression/operator/binary/Equal)
|
||||
return Equal(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/NotEqual)
|
||||
return NotEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Greater)
|
||||
return Greater(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Less)
|
||||
return Less(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/GreaterOrEqual)
|
||||
return GreaterOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LessOrEqual)
|
||||
return LessOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalAnd)
|
||||
return LogicalAnd(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalOr)
|
||||
return LogicalOr(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalXor)
|
||||
return LogicalXor(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseAnd)
|
||||
return BitwiseAnd(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseOr)
|
||||
return BitwiseOr(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseXor)
|
||||
return BitwiseXor(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Add)
|
||||
return Add(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Subtract)
|
||||
return Subtract(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Multiply)
|
||||
return Multiply(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Divide)
|
||||
return Divide(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Power)
|
||||
return Power(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Modulo)
|
||||
return Modulo(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
catch
|
||||
RaiseError(new/datum/runtimeError/TypeMismatch(bin.token, Eval(bin.exp), Eval(bin.exp2)))
|
||||
|
||||
else
|
||||
try
|
||||
switch(exp.type)
|
||||
if(/datum/node/expression/operator/unary/Minus)
|
||||
return Minus(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/LogicalNot)
|
||||
return LogicalNot(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/BitwiseNot)
|
||||
return BitwiseNot(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/group)
|
||||
return Eval(exp.exp)
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
catch
|
||||
RaiseError(new/datum/runtimeError/TypeMismatch/unary(exp.token, Eval(exp.exp)))
|
||||
|
||||
//Binary//
|
||||
//Comparison operators
|
||||
/datum/n_Interpreter/proc/Equal(a, b)
|
||||
return a == b
|
||||
|
||||
/datum/n_Interpreter/proc/NotEqual(a, b)
|
||||
return a != b //LogicalNot(Equal(a, b))
|
||||
|
||||
/datum/n_Interpreter/proc/Greater(a, b)
|
||||
return a > b
|
||||
|
||||
/datum/n_Interpreter/proc/Less(a, b)
|
||||
return a < b
|
||||
|
||||
/datum/n_Interpreter/proc/GreaterOrEqual(a, b)
|
||||
return a >= b
|
||||
|
||||
/datum/n_Interpreter/proc/LessOrEqual(a, b)
|
||||
return a <= b
|
||||
|
||||
//Logical Operators
|
||||
/datum/n_Interpreter/proc/LogicalAnd(a, b)
|
||||
return a && b
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalOr(a, b)
|
||||
return a || b
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalXor(a, b)
|
||||
return (a || b) && !(a && b)
|
||||
|
||||
//Bitwise Operators
|
||||
/datum/n_Interpreter/proc/BitwiseAnd(a, b)
|
||||
return a & b
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseOr(a, b)
|
||||
return a | b
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseXor(a, b)
|
||||
return a ^ b
|
||||
|
||||
//Arithmetic Operators
|
||||
/datum/n_Interpreter/proc/Add(a, b)
|
||||
return a + b
|
||||
|
||||
/datum/n_Interpreter/proc/Subtract(a, b)
|
||||
return a - b
|
||||
|
||||
/datum/n_Interpreter/proc/Divide(a, b)
|
||||
if(b == 0)
|
||||
RaiseError(new/datum/runtimeError/DivisionByZero())
|
||||
return null
|
||||
|
||||
return a / b
|
||||
|
||||
/datum/n_Interpreter/proc/Multiply(a, b)
|
||||
return a * b
|
||||
|
||||
/datum/n_Interpreter/proc/Modulo(a, b)
|
||||
|
||||
return a % b
|
||||
|
||||
/datum/n_Interpreter/proc/Power(a, b)
|
||||
return a ** b
|
||||
|
||||
//Unary//
|
||||
/datum/n_Interpreter/proc/Minus(a)
|
||||
return -a
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalNot(a)
|
||||
return !a
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseNot(a)
|
||||
return ~a
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
File: Interpreter (Public)
|
||||
Contains methods for interacting with the interpreter.
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
||||
*/
|
||||
|
||||
/*
|
||||
Proc: Load
|
||||
Loads a 'compiled' script into memory.
|
||||
|
||||
Parameters:
|
||||
program - A <GlobalBlock> object which represents the script's global scope.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/Load(var/datum/node/BlockDefinition/GlobalBlock/program)
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
alertadmins = 0 // reset admin alerts
|
||||
|
||||
/*
|
||||
Proc: Run
|
||||
Runs the script.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
cur_statements = 0 // reset CPU tracking
|
||||
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a global variable for the duration of the next execution of a script.
|
||||
|
||||
Notes:
|
||||
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
|
||||
while those defined from the block object persist if it is ran multiple times.
|
||||
|
||||
See Also:
|
||||
- <Block.SetVar()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/SetVar(name, value)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid variable name")
|
||||
return
|
||||
|
||||
AssignVariable(name, value)
|
||||
|
||||
/*
|
||||
Proc: SetProc
|
||||
Defines a procedure to be available to the script.
|
||||
|
||||
Parameters:
|
||||
name - The name of the procedure as exposed to the script.
|
||||
path - The typepath of a proc to be called when the function call is read by the interpreter, or, if object is specified, a string representing the procedure's name.
|
||||
object - (Optional) An object which will the be target of a function call.
|
||||
params - Only required if object is not null, a list of the names of parameters the proc takes.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/SetProc(name, path, object = null, list/params = null)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid function name")
|
||||
return
|
||||
|
||||
if(!object)
|
||||
globalScope.functions[name] = path
|
||||
|
||||
else
|
||||
var/datum/node/statement/FunctionDefinition/S = new()
|
||||
S.func_name = name
|
||||
S.parameters = params
|
||||
S.block = new()
|
||||
S.block.SetVar("src", object)
|
||||
var/datum/node/expression/FunctionCall/C = new()
|
||||
C.func_name = path
|
||||
C.object = new("src")
|
||||
for(var/p in params)
|
||||
C.parameters += new/datum/node/expression/value/variable(p)
|
||||
|
||||
var/datum/node/statement/ReturnStatement/R = new()
|
||||
R.value = C
|
||||
S.block.statements += R
|
||||
globalScope.functions[name] = S
|
||||
/*
|
||||
Proc: VarExists
|
||||
Checks whether a global variable with the specified name exists.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/VarExists(name)
|
||||
return globalScope.variables.Find(name) //convert to 1/0 first?
|
||||
|
||||
/*
|
||||
Proc: ProcExists
|
||||
Checks whether a global function with the specified name exists.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/ProcExists(name)
|
||||
return globalScope.functions.Find(name)
|
||||
|
||||
/*
|
||||
Proc: GetVar
|
||||
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
|
||||
|
||||
See Also:
|
||||
- <VarExists()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/GetVar(name)
|
||||
if(!VarExists(name))
|
||||
//CRASH("No variable named '[name]'.")
|
||||
return
|
||||
var/x = globalScope.variables[name]
|
||||
return Eval(x)
|
||||
|
||||
/*
|
||||
Proc: GetCleanVar
|
||||
Returns the value of a global variable in the script and cleans it (sanitizes).
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/GetCleanVar(name, compare)
|
||||
var/x = GetVar(name)
|
||||
if(istext(x) && compare && x != compare) // Was changed
|
||||
x = sanitize(x)
|
||||
return x
|
||||
|
||||
/*
|
||||
Proc: CallProc
|
||||
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function
|
||||
exists before calling this procedure.
|
||||
|
||||
See Also:
|
||||
- <ProcExists()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/CallProc(name, params[]=null)
|
||||
if(!ProcExists(name))
|
||||
//CRASH("No function named '[name]'.")
|
||||
return
|
||||
|
||||
var/datum/node/statement/FunctionDefinition/func = globalScope.functions[name]
|
||||
|
||||
if(istype(func))
|
||||
var/datum/node/statement/FunctionCall/stmt = new
|
||||
stmt.func_name = func.func_name
|
||||
stmt.parameters = params
|
||||
return RunFunction(stmt)
|
||||
|
||||
else
|
||||
return call(func)(arglist(params))
|
||||
//CRASH("Unknown function type '[name]'.")
|
||||
|
||||
/*
|
||||
Event: HandleError
|
||||
Called when the interpreter throws a runtime error.
|
||||
|
||||
See Also:
|
||||
- <runtimeError>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/HandleError(var/datum/runtimeError/e)
|
||||
@@ -1,380 +0,0 @@
|
||||
/*
|
||||
File: Interpreter (Internal)
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
*/
|
||||
/*
|
||||
Macros: Status Macros
|
||||
RETURNING - Indicates that the current function is returning a value.
|
||||
BREAKING - Indicates that the current loop is being terminated.
|
||||
CONTINUING - Indicates that the rest of the current iteration of a loop is being skipped.
|
||||
*/
|
||||
#define RETURNING 1
|
||||
#define BREAKING 2
|
||||
#define CONTINUING 4
|
||||
|
||||
/datum/n_Interpreter
|
||||
var/datum/scope/curScope
|
||||
var/datum/scope/globalScope
|
||||
|
||||
var/datum/node/BlockDefinition/program
|
||||
var/datum/node/statement/FunctionDefinition/curFunction
|
||||
var/datum/stack/scopes = new()
|
||||
var/datum/stack/functions = new()
|
||||
|
||||
var/datum/container // associated container for interpeter
|
||||
/*
|
||||
Var: status
|
||||
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>.
|
||||
*/
|
||||
var/status = 0
|
||||
var/returnVal
|
||||
|
||||
var/max_statements = 900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
||||
var/cur_statements = 0 // current amount of statements called
|
||||
var/alertadmins = 0 // set to 1 if the admins shouldn't be notified of anymore issues
|
||||
var/max_iterations = 100 // max number of uninterrupted loops possible
|
||||
var/max_recursion = 10 // max recursions without returning anything (or completing the code block)
|
||||
var/cur_recursion = 0 // current amount of recursion
|
||||
/*
|
||||
Var: persist
|
||||
If 0, global variables will be reset after Run() finishes.
|
||||
*/
|
||||
var/persist = 1
|
||||
var/paused = 0
|
||||
|
||||
/*
|
||||
Constructor: New
|
||||
Calls <Load()> with the given parameters.
|
||||
*/
|
||||
/datum/n_Interpreter/New(datum/node/BlockDefinition/GlobalBlock/program = null)
|
||||
. = ..()
|
||||
if(program)
|
||||
Load(program)
|
||||
|
||||
/*
|
||||
Set ourselves to Garbage Collect
|
||||
*/
|
||||
/datum/n_Interpreter/proc/GC()
|
||||
..()
|
||||
container = null
|
||||
|
||||
/*
|
||||
Proc: RaiseError
|
||||
Raises a runtime error.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RaiseError(datum/runtimeError/e)
|
||||
e.stack = functions.Copy()
|
||||
e.stack.Push(curFunction)
|
||||
src.HandleError(e)
|
||||
|
||||
/datum/n_Interpreter/proc/CreateScope(datum/node/BlockDefinition/B)
|
||||
var/datum/scope/S = new(B, curScope)
|
||||
scopes.Push(curScope)
|
||||
curScope = S
|
||||
return S
|
||||
|
||||
/datum/n_Interpreter/proc/CreateGlobalScope()
|
||||
scopes.Clear()
|
||||
var/datum/scope/S = new(program, null)
|
||||
globalScope = S
|
||||
return S
|
||||
|
||||
/*
|
||||
Proc: AlertAdmins
|
||||
Alerts the admins of a script that is bad.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/AlertAdmins()
|
||||
if(container && !alertadmins)
|
||||
if(istype(container, /datum/TCS_Compiler))
|
||||
var/datum/TCS_Compiler/Compiler = container
|
||||
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
|
||||
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
|
||||
|
||||
alertadmins = 1
|
||||
message_admins(message, 1)
|
||||
/*
|
||||
Proc: RunBlock
|
||||
Runs each statement in a block of code.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunBlock(var/datum/node/BlockDefinition/Block, var/datum/scope/scope = null)
|
||||
var/is_global = istype(Block, /datum/node/BlockDefinition/GlobalBlock)
|
||||
if(!is_global)
|
||||
if(scope)
|
||||
curScope = scope
|
||||
else
|
||||
CreateScope(Block)
|
||||
else
|
||||
if(!persist)
|
||||
CreateGlobalScope()
|
||||
|
||||
curScope = globalScope
|
||||
|
||||
if(cur_statements < max_statements)
|
||||
for(var/datum/node/statement/S in Block.statements)
|
||||
while(paused) sleep(10)
|
||||
|
||||
cur_statements++
|
||||
if(cur_statements >= max_statements)
|
||||
RaiseError(new/datum/runtimeError/MaxCPU())
|
||||
AlertAdmins()
|
||||
break
|
||||
|
||||
if(istype(S, /datum/node/statement/VariableAssignment))
|
||||
var/datum/node/statement/VariableAssignment/stmt = S
|
||||
var/name = stmt.var_name.id_name
|
||||
|
||||
if(!stmt.object)
|
||||
// Below we assign the variable first to null if it doesn't already exist.
|
||||
// This is necessary for assignments like +=, and when the variable is used in a function
|
||||
// If the variable already exists in a different block, then AssignVariable will automatically use that one.
|
||||
if(!IsVariableAccessible(name))
|
||||
AssignVariable(name, null)
|
||||
|
||||
AssignVariable(name, Eval(stmt.value))
|
||||
else
|
||||
var/datum/D = Eval(GetVariable(stmt.object.id_name))
|
||||
if(!D)
|
||||
return
|
||||
|
||||
D.vars[stmt.var_name.id_name] = Eval(stmt.value)
|
||||
|
||||
else if(istype(S, /datum/node/statement/VariableDeclaration))
|
||||
//VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
|
||||
var/datum/node/statement/VariableDeclaration/dec=S
|
||||
if(!dec.object)
|
||||
AssignVariable(dec.var_name.id_name, null, curScope)
|
||||
else
|
||||
var/datum/D = Eval(GetVariable(dec.object.id_name))
|
||||
if(!D)
|
||||
return
|
||||
|
||||
D.vars[dec.var_name.id_name] = null
|
||||
|
||||
else if(istype(S, /datum/node/statement/FunctionCall))
|
||||
RunFunction(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/FunctionDefinition))
|
||||
//do nothing
|
||||
|
||||
else if(istype(S, /datum/node/statement/WhileLoop))
|
||||
RunWhile(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/IfStatement))
|
||||
RunIf(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/ReturnStatement))
|
||||
if(!curFunction)
|
||||
RaiseError(new/datum/runtimeError/UnexpectedReturn())
|
||||
continue
|
||||
|
||||
status |= RETURNING
|
||||
returnVal = Eval(S:value)
|
||||
break
|
||||
|
||||
else if(istype(S, /datum/node/statement/BreakStatement))
|
||||
status |= BREAKING
|
||||
break
|
||||
|
||||
else if(istype(S, /datum/node/statement/ContinueStatement))
|
||||
status |= CONTINUING
|
||||
break
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
if(status)
|
||||
break
|
||||
|
||||
curScope = scopes.Pop()
|
||||
|
||||
/*
|
||||
Proc: RunFunction
|
||||
Runs a function block or a proc with the arguments specified in the script.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunFunction(var/datum/node/statement/FunctionCall/stmt)
|
||||
//Note that anywhere /datum/node/statement/FunctionCall/stmt is used so may /datum/node/expression/FunctionCall
|
||||
|
||||
// If recursion gets too high (max 50 nested functions) throw an error
|
||||
if(cur_recursion >= max_recursion)
|
||||
AlertAdmins()
|
||||
RaiseError(new/datum/runtimeError/RecursionLimitReached())
|
||||
return 0
|
||||
|
||||
var/datum/node/statement/FunctionDefinition/def
|
||||
if(!stmt.object) //A scope's function is being called, stmt.object is null
|
||||
def = GetFunction(stmt.func_name)
|
||||
|
||||
else if(istype(stmt.object)) //A method of an object exposed as a variable is being called, stmt.object is a /node/identifier
|
||||
var/O = GetVariable(stmt.object.id_name) //Gets a reference to the object which is the target of the function call.
|
||||
if(!O) return //Error already thrown in GetVariable()
|
||||
def = Eval(O)
|
||||
|
||||
if(!def)
|
||||
return
|
||||
|
||||
cur_recursion++ // add recursion
|
||||
if(istype(def))
|
||||
if(curFunction) functions.Push(curFunction)
|
||||
var/datum/scope/S = CreateScope(def.block)
|
||||
|
||||
for(var/i = 1 to def.parameters.len)
|
||||
var/val
|
||||
if(stmt.parameters.len >= i)
|
||||
val = stmt.parameters[i]
|
||||
//else
|
||||
// unspecified param
|
||||
AssignVariable(def.parameters[i], new/datum/node/expression/value/literal(Eval(val)), S)
|
||||
|
||||
curFunction = stmt
|
||||
RunBlock(def.block, S)
|
||||
|
||||
//Handle return value
|
||||
. = returnVal
|
||||
status &= ~RETURNING
|
||||
returnVal = null
|
||||
curFunction = functions.Pop()
|
||||
cur_recursion--
|
||||
|
||||
else
|
||||
cur_recursion--
|
||||
var/list/params = new
|
||||
for(var/datum/node/expression/P in stmt.parameters)
|
||||
params += list(Eval(P))
|
||||
|
||||
if(isobject(def)) //def is an object which is the target of a function call
|
||||
if(!hascall(def, stmt.func_name))
|
||||
RaiseError(new/datum/runtimeError/UndefinedFunction("[stmt.object.id_name].[stmt.func_name]"))
|
||||
return
|
||||
|
||||
return call(def, stmt.func_name)(arglist(params))
|
||||
|
||||
else //def is a path to a global proc
|
||||
return call(def)(arglist(params))
|
||||
//else
|
||||
// RaiseError(new/runtimeError/UnknownInstruction())
|
||||
|
||||
/*
|
||||
Proc: RunIf
|
||||
Checks a condition and runs either the if block or else block.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunIf(var/datum/node/statement/IfStatement/stmt)
|
||||
if(!stmt.skip)
|
||||
if(Eval(stmt.cond))
|
||||
RunBlock(stmt.block)
|
||||
// Loop through the if else chain and tell them to be skipped.
|
||||
var/datum/node/statement/IfStatement/i = stmt.else_if
|
||||
var/fail_safe = 800
|
||||
|
||||
while(i && fail_safe)
|
||||
fail_safe -= 1
|
||||
i.skip = 1
|
||||
i = i.else_if
|
||||
|
||||
else if(stmt.else_block)
|
||||
RunBlock(stmt.else_block)
|
||||
|
||||
// We don't need to skip you anymore.
|
||||
stmt.skip = 0
|
||||
|
||||
/*
|
||||
Proc: RunWhile
|
||||
Runs a while loop.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/RunWhile(var/datum/node/statement/WhileLoop/stmt)
|
||||
var/i = 1
|
||||
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
|
||||
continue
|
||||
|
||||
status &= ~BREAKING
|
||||
|
||||
/*
|
||||
Proc:Iterate
|
||||
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/Iterate(var/datum/node/BlockDefinition/block, count)
|
||||
RunBlock(block)
|
||||
if(max_iterations > 0 && count >= max_iterations)
|
||||
RaiseError(new/datum/runtimeError/IterationLimitReached())
|
||||
return 0
|
||||
|
||||
if(status & (BREAKING|RETURNING))
|
||||
return 0
|
||||
|
||||
status &= ~CONTINUING
|
||||
return 1
|
||||
|
||||
/*
|
||||
Proc: GetFunction
|
||||
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/GetFunction(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.functions.Find(name))
|
||||
return S.functions[name]
|
||||
S = S.parent
|
||||
|
||||
RaiseError(new/datum/runtimeError/UndefinedFunction(name))
|
||||
|
||||
/*
|
||||
Proc: GetVariable
|
||||
Finds a variable in an accessible scope and returns its value.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/GetVariable(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return S.variables[name]
|
||||
S = S.parent
|
||||
|
||||
RaiseError(new/datum/runtimeError/UndefinedVariable(name))
|
||||
|
||||
/datum/n_Interpreter/proc/GetVariableScope(name) //needed for when you reassign a variable in a higher scope
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return S
|
||||
|
||||
S = S.parent
|
||||
|
||||
|
||||
/datum/n_Interpreter/proc/IsVariableAccessible(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return TRUE
|
||||
S = S.parent
|
||||
|
||||
return FALSE
|
||||
|
||||
|
||||
/*
|
||||
Proc: AssignVariable
|
||||
Assigns a value to a variable in a specific block.
|
||||
|
||||
Parameters:
|
||||
name - The name of the variable to assign.
|
||||
value - The value to assign to it.
|
||||
S - The scope the variable resides in. If it is null, a scope with the variable already existing is found. If no scopes have a variable of the given name, the current scope is used.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/AssignVariable(name, datum/node/expression/value, var/datum/scope/S = null)
|
||||
if(!S) S = GetVariableScope(name)
|
||||
if(!S) S = curScope
|
||||
if(!S) S = globalScope
|
||||
|
||||
ASSERT(istype(S))
|
||||
if(istext(value) || isnum(value) || isnull(value)) value = new/datum/node/expression/value/literal(value)
|
||||
else if(!istype(value) && isobject(value)) value = new/datum/node/expression/value/reference(value)
|
||||
//TODO: check for invalid name
|
||||
S.variables["[name]"] = value
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
Class: scope
|
||||
A runtime instance of a block. Used internally by the interpreter.
|
||||
*/
|
||||
/datum/scope
|
||||
var/datum/scope/parent = null
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/list/functions
|
||||
var/list/variables
|
||||
|
||||
/datum/scope/New(var/datum/node/BlockDefinition/B, var/datum/scope/parent)
|
||||
src.block = B
|
||||
src.parent = parent
|
||||
src.variables = B.initial_variables.Copy()
|
||||
src.functions = B.functions.Copy()
|
||||
. = ..()
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
File: Options
|
||||
*/
|
||||
// Ascii values of characters
|
||||
/var/const/ascii_A = 65
|
||||
/var/const/ascii_Z = 90
|
||||
/var/const/ascii_a = 97
|
||||
/var/const/ascii_z = 122
|
||||
/var/const/ascii_DOLLAR = 36 // $
|
||||
/var/const/ascii_ZERO = 48
|
||||
/var/const/ascii_NINE = 57
|
||||
/var/const/ascii_UNDERSCORE = 95 // _
|
||||
|
||||
/*
|
||||
Class: n_scriptOptions
|
||||
*/
|
||||
/datum/n_scriptOptions/proc/CanStartID(char) //returns true if the character can start a variable, function, or keyword name (by default letters or an underscore)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return (char in ascii_A to ascii_Z) || (char in ascii_a to ascii_z) || char == ascii_UNDERSCORE || char == ascii_DOLLAR
|
||||
|
||||
/datum/n_scriptOptions/proc/IsValidIDChar(char) //returns true if the character can be in the body of a variable, function, or keyword name (by default letters, numbers, and underscore)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return CanStartID(char) || IsDigit(char)
|
||||
|
||||
/datum/n_scriptOptions/proc/IsDigit(char)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return char in ascii_ZERO to ascii_NINE
|
||||
|
||||
/datum/n_scriptOptions/proc/IsValidID(id) //returns true if all the characters in the string are okay to be in an identifier name
|
||||
if(!CanStartID(id)) //don't need to grab first char in id, since text2ascii does it automatically
|
||||
return 0
|
||||
|
||||
var/list/charmap = string2charlist(id)
|
||||
if(charmap.len == 1)
|
||||
return 1
|
||||
|
||||
for(var/i = 2 to charmap.len)
|
||||
if(!IsValidIDChar(charmap[i]))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/*
|
||||
Class: nS_Options
|
||||
An implementation of <n_scriptOptions> for the n_Script language.
|
||||
*/
|
||||
/datum/n_scriptOptions/nS_Options
|
||||
var/list/symbols = list(
|
||||
"(",
|
||||
")",
|
||||
"\[",
|
||||
"]",
|
||||
";",
|
||||
",",
|
||||
"{",
|
||||
"}"
|
||||
) //scanner - Characters that can be in symbols
|
||||
/*
|
||||
Var: keywords
|
||||
An associative list used by the parser to parse keywords. Indices are strings which will trigger the keyword when parsed and the
|
||||
associated values are <nS_Keyword> types of which the <n_Keyword.Parse()> proc will be called.
|
||||
*/
|
||||
var/list/keywords = list(
|
||||
"if" = /datum/n_Keyword/nS_Keyword/kwIf,
|
||||
"else" = /datum/n_Keyword/nS_Keyword/kwElse,
|
||||
"elseif" = /datum/n_Keyword/nS_Keyword/kwElseIf,
|
||||
"while" = /datum/n_Keyword/nS_Keyword/kwWhile,
|
||||
"break" = /datum/n_Keyword/nS_Keyword/kwBreak,
|
||||
"continue" = /datum/n_Keyword/nS_Keyword/kwContinue,
|
||||
"return" = /datum/n_Keyword/nS_Keyword/kwReturn,
|
||||
"def" = /datum/n_Keyword/nS_Keyword/kwDef
|
||||
)
|
||||
|
||||
var/list/assign_operators = list(
|
||||
"=" = null,
|
||||
"&=" = "&",
|
||||
"|=" = "|",
|
||||
"`=" = "`",
|
||||
"+=" = "+",
|
||||
"-=" = "-",
|
||||
"*=" = "*",
|
||||
"/=" = "/",
|
||||
"^=" = "^",
|
||||
"%=" = "%"
|
||||
)
|
||||
|
||||
var/list/unary_operators = list(
|
||||
"!" = /datum/node/expression/operator/unary/LogicalNot,
|
||||
"~" = /datum/node/expression/operator/unary/BitwiseNot,
|
||||
"-" = /datum/node/expression/operator/unary/Minus
|
||||
)
|
||||
|
||||
var/list/binary_operators = list(
|
||||
"==" = /datum/node/expression/operator/binary/Equal,
|
||||
"!=" = /datum/node/expression/operator/binary/NotEqual,
|
||||
">" = /datum/node/expression/operator/binary/Greater,
|
||||
"<" = /datum/node/expression/operator/binary/Less,
|
||||
">=" = /datum/node/expression/operator/binary/GreaterOrEqual,
|
||||
"<=" = /datum/node/expression/operator/binary/LessOrEqual,
|
||||
"&&" = /datum/node/expression/operator/binary/LogicalAnd,
|
||||
"||" = /datum/node/expression/operator/binary/LogicalOr,
|
||||
"&" = /datum/node/expression/operator/binary/BitwiseAnd,
|
||||
"|" = /datum/node/expression/operator/binary/BitwiseOr,
|
||||
"`" = /datum/node/expression/operator/binary/BitwiseXor,
|
||||
"+" = /datum/node/expression/operator/binary/Add,
|
||||
"-" = /datum/node/expression/operator/binary/Subtract,
|
||||
"*" = /datum/node/expression/operator/binary/Multiply,
|
||||
"/" = /datum/node/expression/operator/binary/Divide,
|
||||
"^" = /datum/node/expression/operator/binary/Power,
|
||||
"%" = /datum/node/expression/operator/binary/Modulo
|
||||
)
|
||||
|
||||
/datum/n_scriptOptions/nS_Options/New()
|
||||
. = ..()
|
||||
for(var/O in assign_operators + binary_operators + unary_operators)
|
||||
if(!symbols.Find(O))
|
||||
symbols += O
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
File: Expressions
|
||||
Procedures for parsing expressions.
|
||||
*/
|
||||
|
||||
/*
|
||||
Macros: Expression Macros
|
||||
OPERATOR - A value indicating the parser currently expects a binary operator.
|
||||
VALUE - A value indicating the parser currently expects a value.
|
||||
SHIFT - Tells the parser to push the current operator onto the stack.
|
||||
REDUCE - Tells the parser to reduce the stack.
|
||||
*/
|
||||
#define OPERATOR 1
|
||||
#define VALUE 2
|
||||
#define SHIFT 0
|
||||
#define REDUCE 1
|
||||
|
||||
/*
|
||||
Class: nS_Parser
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser
|
||||
/*
|
||||
Var: expecting
|
||||
A variable which keeps track of whether an operator or value is expected. It should be either <OPERATOR> or <VALUE>. See <ParseExpression()>
|
||||
for more information.
|
||||
*/
|
||||
var/expecting = VALUE
|
||||
|
||||
/*
|
||||
Proc: Precedence
|
||||
Compares two operators, decides which is higher in the order of operations, and returns <SHIFT> or <REDUCE>.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/Precedence(var/datum/node/expression/operator/top, var/datum/node/expression/operator/input)
|
||||
if(istype(top))
|
||||
top = top.precedence
|
||||
|
||||
if(istype(input))
|
||||
|
||||
input = input:precedence
|
||||
|
||||
if(top >= input)
|
||||
return REDUCE
|
||||
|
||||
return SHIFT
|
||||
|
||||
/*
|
||||
Proc: GetExpression
|
||||
Takes a token expected to represent a value and returns an <expression> node.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetExpression(var/datum/token/T)
|
||||
if(!T)
|
||||
return
|
||||
|
||||
if(istype(T, /datum/node/expression))
|
||||
return T
|
||||
|
||||
switch(T.type)
|
||||
if(/datum/token/word)
|
||||
return new/datum/node/expression/value/variable(T.value)
|
||||
|
||||
if(/datum/token/accessor)
|
||||
var/datum/token/accessor/A = T
|
||||
var/datum/node/expression/value/variable/E// = new(A.member)
|
||||
var/datum/stack/S = new()
|
||||
|
||||
while(istype(A.object, /datum/token/accessor))
|
||||
S.Push(A)
|
||||
A = A.object
|
||||
|
||||
ASSERT(istext(A.object))
|
||||
|
||||
while(A)
|
||||
var/datum/node/expression/value/variable/V = new()
|
||||
V.id = new(A.member)
|
||||
if(E)
|
||||
V.object = E
|
||||
else
|
||||
V.object = new/datum/node/identifier(A.object)
|
||||
|
||||
E = V
|
||||
A = S.Pop()
|
||||
return E
|
||||
|
||||
if(/datum/token/number, /datum/token/string)
|
||||
return new/datum/node/expression/value/literal(T.value)
|
||||
|
||||
/*
|
||||
Proc: GetOperator
|
||||
Gets a path related to a token or string and returns an instance of the given type. This is used to get an instance of either a binary or unary
|
||||
operator from a token.
|
||||
|
||||
Parameters:
|
||||
O - The input value. If this is a token, O is reset to the token's value.
|
||||
When O is a string and is in L, its associated value is used as the path to instantiate.
|
||||
type - The desired type of the returned object.
|
||||
L - The list in which to search for O.
|
||||
|
||||
See Also:
|
||||
- <GetBinaryOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetOperator(O, type = /datum/node/expression/operator, L[])
|
||||
if(istype(O, type))
|
||||
return O //O is already the desired type
|
||||
|
||||
if(istype(O, /datum/token))
|
||||
O = O:value //sets O to text
|
||||
|
||||
if(istext(O)) //sets O to path
|
||||
if(L.Find(O))
|
||||
O = L[O]
|
||||
else
|
||||
return null
|
||||
|
||||
if(ispath(O))
|
||||
O = new O //catches path from last check
|
||||
|
||||
else
|
||||
return null //Unknown type
|
||||
|
||||
return O
|
||||
|
||||
/*
|
||||
Proc: GetBinaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a binary operator type with which the given string is associated. For example, if
|
||||
O is set to "+", an <Add> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetBinaryOperator(O)
|
||||
return GetOperator(O, /datum/node/expression/operator/binary, options.binary_operators)
|
||||
|
||||
/*
|
||||
Proc: GetUnaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a unary operator type with which the given string is associated. For example, if
|
||||
O is set to "!", a <LogicalNot> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetBinaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetUnaryOperator(O)
|
||||
return GetOperator(O, /datum/node/expression/operator/unary, options.unary_operators)
|
||||
|
||||
/*
|
||||
Proc: Reduce
|
||||
Takes the operator on top of the opr stack and assigns its operand(s). Then this proc pushes the value of that operation to the top
|
||||
of the val stack.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/Reduce(var/datum/stack/opr, var/datum/stack/val)
|
||||
var/datum/node/expression/operator/O = opr.Pop()
|
||||
if(!O)
|
||||
return
|
||||
|
||||
if(!istype(O))
|
||||
errors += new/datum/scriptError("Error reducing expression - invalid operator.")
|
||||
return
|
||||
|
||||
//Take O and assign its operands, popping one or two values from the val stack
|
||||
//depending on whether O is a binary or unary operator.
|
||||
if(istype(O, /datum/node/expression/operator/binary))
|
||||
var/datum/node/expression/operator/binary/B=O
|
||||
B.exp2 = val.Pop()
|
||||
B.exp = val.Pop()
|
||||
val.Push(B)
|
||||
|
||||
else
|
||||
O.exp=val.Pop()
|
||||
val.Push(O)
|
||||
|
||||
/*
|
||||
Proc: EndOfExpression
|
||||
Returns true if the current token represents the end of an expression.
|
||||
|
||||
Parameters:
|
||||
end - A list of values to compare the current token to.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/EndOfExpression(end[])
|
||||
if(!curToken)
|
||||
return 1
|
||||
if(istype(curToken, /datum/token/symbol) && end.Find(curToken.value))
|
||||
return 1
|
||||
if(istype(curToken, /datum/token/end) && end.Find(/datum/token/end))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
Proc: ParseExpression
|
||||
Uses the Shunting-yard algorithm to parse expressions.
|
||||
|
||||
Notes:
|
||||
- When an opening parenthesis is found, then <ParseParenExpression()> is called to handle it.
|
||||
- The <expecting> variable helps distinguish unary operators from binary operators (for cases like the - operator, which can be either).
|
||||
|
||||
Articles:
|
||||
- <http://epaperpress.com/oper/>
|
||||
- <http://en.wikipedia.org/wiki/Shunting-yard_algorithm>
|
||||
|
||||
See Also:
|
||||
- <ParseFunctionExpression()>
|
||||
- <ParseParenExpression()>
|
||||
- <ParseParamExpression()>
|
||||
*/
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseExpression(var/list/end = list(/datum/token/end), list/ErrChars = list("{", "}"), check_functions = 0)
|
||||
var/datum/stack/opr = new
|
||||
var/datum/stack/val = new
|
||||
src.expecting = VALUE
|
||||
var/loop = 0
|
||||
for()
|
||||
loop++
|
||||
if(loop > 800)
|
||||
errors += new/datum/scriptError("Too many nested tokens.")
|
||||
return
|
||||
|
||||
if(EndOfExpression(end))
|
||||
break
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && ErrChars.Find(curToken.value))
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
break
|
||||
|
||||
if(index > tokens.len) //End of File
|
||||
errors += new/datum/scriptError/EndOfFile()
|
||||
break
|
||||
|
||||
var/datum/token/ntok
|
||||
if(index + 1 <= tokens.len)
|
||||
ntok = tokens[index + 1]
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == "(") //Parse parentheses expression
|
||||
if(expecting != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(ParseParenExpression())
|
||||
|
||||
else if(istype(curToken, /datum/token/symbol)) //Operator found.
|
||||
var/datum/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
|
||||
if(src.expecting == OPERATOR)
|
||||
curOperator = GetBinaryOperator(curToken)
|
||||
if(!curOperator)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
else
|
||||
curOperator = GetUnaryOperator(curToken)
|
||||
if(!curOperator) //given symbol isn't a unary operator
|
||||
errors += new/datum/scriptError/ExpectedToken("expression", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
if(opr.Top() && Precedence(opr.Top(), curOperator) == REDUCE) //Check order of operations and reduce if necessary
|
||||
Reduce(opr, val)
|
||||
continue
|
||||
|
||||
opr.Push(curOperator)
|
||||
src.expecting = VALUE
|
||||
|
||||
else if(ntok && ntok.value == "(" && istype(ntok, /datum/token/symbol)\
|
||||
&& istype(curToken, /datum/token/word)) //Parse function call
|
||||
|
||||
if(!check_functions)
|
||||
|
||||
var/datum/token/preToken = curToken
|
||||
var/old_expect = src.expecting
|
||||
var/fex = ParseFunctionExpression()
|
||||
if(old_expect != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(fex)
|
||||
else
|
||||
errors += new/datum/scriptError/ParameterFunction(curToken)
|
||||
break
|
||||
|
||||
else if(istype(curToken, /datum/token/keyword)) //inline keywords
|
||||
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
||||
kw = new kw(inline=1)
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
|
||||
else if(istype(curToken, /datum/token/end)) //semicolon found where it wasn't expected
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
NextToken()
|
||||
continue
|
||||
else
|
||||
if(expecting != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(GetExpression(curToken))
|
||||
src.expecting = OPERATOR
|
||||
|
||||
NextToken()
|
||||
|
||||
while(opr.Top())
|
||||
Reduce(opr, val)
|
||||
//Reduce the value stack completely
|
||||
. = val.Pop() //Return what should be the last value on the stack
|
||||
|
||||
if(val.Top()) //
|
||||
var/datum/node/N = val.Pop()
|
||||
errors += new/datum/scriptError("Error parsing expression. Unexpected value left on stack: [N.ToString()].")
|
||||
return null
|
||||
|
||||
/*
|
||||
Proc: ParseFunctionExpression
|
||||
Parses a function call inside of an expression.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseFunctionExpression()
|
||||
var/datum/node/expression/FunctionCall/exp = new
|
||||
exp.func_name = curToken.value
|
||||
NextToken() //skip function name
|
||||
NextToken() //skip open parenthesis, already found
|
||||
var/loops = 0
|
||||
|
||||
for()
|
||||
loops++
|
||||
if(loops >= 800)
|
||||
errors += new/datum/scriptError("Too many nested expressions.")
|
||||
break
|
||||
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
||||
return exp
|
||||
exp.parameters += ParseParamExpression()
|
||||
if(errors.len)
|
||||
return exp
|
||||
|
||||
if(curToken.value == "," && istype(curToken, /datum/token/symbol))
|
||||
NextToken() //skip comma
|
||||
|
||||
if(istype(curToken, /datum/token/end)) //Prevents infinite loop...
|
||||
errors += new/datum/scriptError/ExpectedToken(")")
|
||||
return exp
|
||||
|
||||
/*
|
||||
Proc: ParseParenExpression
|
||||
Parses an expression that ends with a close parenthesis. This is used for parsing expressions inside of parentheses.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/ParseParenExpression()
|
||||
if(!CheckToken("(", /datum/token/symbol))
|
||||
return
|
||||
return new/datum/node/expression/operator/unary/group(ParseExpression(list(")")))
|
||||
|
||||
/*
|
||||
Proc: ParseParamExpression
|
||||
Parses an expression that ends with either a comma or close parenthesis. This is used for parsing the parameters passed to a function call.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/ParseParamExpression(var/check_functions = 0)
|
||||
var/cf = check_functions
|
||||
return ParseExpression(list(",", ")"), check_functions = cf)
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
File: Keywords
|
||||
*/
|
||||
var/const/KW_FAIL = 0 //Fatal error; stop parsing entire script.
|
||||
var/const/KW_PASS = 1 //OK
|
||||
var/const/KW_ERR = 2 //Non-fatal error, keyword couldn't be handled properly. Ignore keyword but continue on.
|
||||
var/const/KW_WARN = 3 //Warning
|
||||
|
||||
/*
|
||||
var/const/Class: n_Keyword
|
||||
var/const/Represents a special statement in the code triggered by a keyword.
|
||||
*/
|
||||
/datum/n_Keyword
|
||||
/*
|
||||
Var: inline
|
||||
1 if the keyword is in an expression (e.g. the new keyword in many languages), 0 otherwise (such as the if and else keywords).
|
||||
*/
|
||||
var/inline
|
||||
|
||||
/datum/n_Keyword/New(inline = 0)
|
||||
src.inline = inline
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Proc: Parse
|
||||
Called when the parser finds a keyword in the code.
|
||||
|
||||
Parameters:
|
||||
parser - The parser that created this object. You can use the parameter to manipulate the parser in order to add statements and blocks
|
||||
to its AST.
|
||||
*/
|
||||
/datum/n_Keyword/proc/Parse(var/datum/n_Parser/parser)
|
||||
return
|
||||
/*
|
||||
Class: nS_Keyword
|
||||
A keyword in n_Script. By default these include return, if, else, while, and def. To enable or disable a keyword, change the
|
||||
<nS_Options.keywords> list.
|
||||
|
||||
Behavior:
|
||||
When a parser is expecting a new statement, and a keyword listed in <nS_Options.keywords> is found, it will call the keyword's
|
||||
<n_Keyword.Parse()> proc.
|
||||
*/
|
||||
/datum/n_Keyword/nS_Keyword/New(var/inline = 0)
|
||||
if(inline)
|
||||
del src
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwReturn/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/node/BlockDefinition/GlobalBlock)) // Exit out of the program by setting the tokens list size to the same as index.
|
||||
parser.tokens.len = parser.index
|
||||
return
|
||||
|
||||
var/datum/node/statement/ReturnStatement/stmt = new
|
||||
parser.NextToken() //skip 'return' token
|
||||
stmt.value = parser.ParseExpression()
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwIf/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/IfStatement/stmt = new
|
||||
parser.NextToken() //skip 'if' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
|
||||
return KW_ERR
|
||||
|
||||
parser.curBlock.statements += stmt
|
||||
stmt.block = new
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwElseIf/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/list/L = parser.curBlock.statements
|
||||
var/datum/node/statement/IfStatement/ifstmt
|
||||
|
||||
if(L && L.len)
|
||||
ifstmt = L[L.len] //Get the last statement in the current block
|
||||
|
||||
if(!ifstmt || !istype(ifstmt) || ifstmt.else_if)
|
||||
to_chat(usr, "NTSL: ELSE IF FAILED: [!ifstmt], [!istype(ifstmt)], [!istype(ifstmt) || ifstmt.else_if]")// Usr is unsafe as SHIT but JUST incase I forget this debug line like the fucking asset cache...
|
||||
|
||||
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
var/datum/node/statement/IfStatement/ElseIf/stmt = new
|
||||
parser.NextToken() //skip 'if' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip = 0)) //Token needs to be preserved for parse loop, so skip=0
|
||||
return KW_ERR
|
||||
|
||||
parser.curBlock.statements += stmt
|
||||
stmt.block = new
|
||||
ifstmt.else_if = stmt
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwElse/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/list/L = parser.curBlock.statements
|
||||
var/datum/node/statement/IfStatement/stmt
|
||||
|
||||
if(L && L.len)
|
||||
stmt = L[L.len] //Get the last statement in the current block
|
||||
|
||||
if(!stmt || !istype(stmt) || stmt.else_block) //Ensure that it is an if statement
|
||||
to_chat(usr, "NTSL: ELSE IF FAILED: [!stmt], [!istype(stmt)], [!istype(stmt) || stmt.else_block]")// Usr is unsafe as SHIT but JUST incase I forget this debug line like the fucking asset cache...
|
||||
|
||||
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
parser.NextToken() //skip 'else' token
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip = 0))
|
||||
return KW_ERR
|
||||
|
||||
stmt.else_block = new()
|
||||
parser.AddBlock(stmt.else_block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwWhile/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/WhileLoop/stmt = new
|
||||
parser.NextToken() //skip 'while' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip=0))
|
||||
return KW_ERR
|
||||
|
||||
parser.curBlock.statements += stmt
|
||||
stmt.block = new
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwBreak/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/node/BlockDefinition/GlobalBlock))
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
|
||||
var/datum/node/statement/BreakStatement/stmt = new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwContinue/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/node/BlockDefinition/GlobalBlock))
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
|
||||
var/datum/node/statement/ContinueStatement/stmt = new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwDef/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/FunctionDefinition/def = new
|
||||
parser.NextToken() //skip 'def' token
|
||||
|
||||
if(!parser.options.IsValidID(parser.curToken.value))
|
||||
parser.errors += new/datum/scriptError/InvalidID(parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
def.func_name = parser.curToken.value
|
||||
parser.NextToken()
|
||||
|
||||
if(!parser.CheckToken("(", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
for() //for now parameters can be separated by whitespace - they don't need a comma in between
|
||||
if(istype(parser.curToken, /datum/token/symbol))
|
||||
switch(parser.curToken.value)
|
||||
if(",")
|
||||
parser.NextToken()
|
||||
if(")")
|
||||
break
|
||||
else
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
return KW_ERR
|
||||
|
||||
else if(istype(parser.curToken, /datum/token/word))
|
||||
def.parameters += parser.curToken.value
|
||||
parser.NextToken()
|
||||
|
||||
else
|
||||
parser.errors += new/datum/scriptError/InvalidID(parser.curToken)
|
||||
return KW_ERR
|
||||
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(istype(parser.curToken, /datum/token/end)) //Function prototype
|
||||
parser.curBlock.statements += def
|
||||
|
||||
else if(parser.curToken.value == "{" && istype(parser.curToken, /datum/token/symbol))
|
||||
def.block = new
|
||||
parser.curBlock.statements += def
|
||||
parser.curBlock.functions[def.func_name] = def
|
||||
parser.AddBlock(def.block)
|
||||
else
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
return KW_FAIL
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
File: Parser
|
||||
*/
|
||||
/*
|
||||
Class: n_Parser
|
||||
An object that reads tokens and produces an AST (abstract syntax tree).
|
||||
*/
|
||||
/datum/n_Parser
|
||||
/*
|
||||
Var: index
|
||||
The parser's current position in the token's list.
|
||||
*/
|
||||
var/index = 1
|
||||
/*
|
||||
Var: tokens
|
||||
A list of tokens in the source code generated by a scanner.
|
||||
*/
|
||||
var/list/tokens = new
|
||||
/*
|
||||
Var: errors
|
||||
A list of fatal errors found by the parser. If there are any items in this list, then it is not safe to run the returned AST.
|
||||
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the script.
|
||||
*/
|
||||
var/list/warnings = new
|
||||
/*
|
||||
Var: curToken
|
||||
The token at <index> in <tokens>.
|
||||
*/
|
||||
var/datum/token/curToken
|
||||
var/datum/stack/blocks = new
|
||||
var/datum/node/BlockDefinition/GlobalBlock/global_block = new
|
||||
var/datum/node/BlockDefinition/GlobalBlock/curBlock
|
||||
|
||||
/*
|
||||
Proc: Parse
|
||||
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
||||
*/
|
||||
/datum/n_Parser/proc/Parse()
|
||||
return
|
||||
/*
|
||||
Proc: NextToken
|
||||
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
||||
*/
|
||||
/datum/n_Parser/proc/NextToken()
|
||||
if(index >= tokens.len)
|
||||
curToken = null
|
||||
else
|
||||
curToken = tokens[++index]
|
||||
return curToken
|
||||
|
||||
/*
|
||||
Class: nS_Parser
|
||||
An implmentation of a parser for n_Script.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
/*
|
||||
Constructor: New
|
||||
|
||||
Parameters:
|
||||
tokens - A list of tokens to parse.
|
||||
options - An object used for configuration.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/New(tokens[], var/datum/n_scriptOptions/options)
|
||||
src.tokens = tokens
|
||||
src.options = options
|
||||
curBlock = global_block
|
||||
return ..()
|
||||
|
||||
/datum/n_Parser/nS_Parser/Parse()
|
||||
ASSERT(tokens)
|
||||
for(,src.index <= src.tokens.len, src.index++)
|
||||
curToken = tokens[index]
|
||||
switch(curToken.type)
|
||||
if(/datum/token/keyword)
|
||||
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
||||
kw = new kw()
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
|
||||
if(/datum/token/word)
|
||||
var/datum/token/ntok
|
||||
if(index + 1 > tokens.len)
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
ntok = tokens[index + 1]
|
||||
|
||||
if(!istype(ntok, /datum/token/symbol))
|
||||
errors += new/datum/scriptError/BadToken(ntok)
|
||||
continue
|
||||
|
||||
if(ntok.value == "(")
|
||||
ParseFunctionStatement()
|
||||
|
||||
else if(options.assign_operators.Find(ntok.value))
|
||||
ParseAssignment()
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(ntok)
|
||||
continue
|
||||
|
||||
if(!istype(curToken, /datum/token/end))
|
||||
errors += new/datum/scriptError/ExpectedToken(";", curToken)
|
||||
continue
|
||||
|
||||
if(/datum/token/symbol)
|
||||
if(curToken.value == "}")
|
||||
if(!EndBlock())
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
if(/datum/token/end)
|
||||
warnings += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
return
|
||||
|
||||
return global_block
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/CheckToken(val, type, err = 1, skip = 1)
|
||||
if(curToken.value != val || !istype(curToken, type))
|
||||
if(err)
|
||||
errors += new/datum/scriptError/ExpectedToken(val, curToken)
|
||||
return 0
|
||||
if(skip)
|
||||
NextToken()
|
||||
|
||||
return 1
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/AddBlock(var/datum/node/BlockDefinition/B)
|
||||
blocks.Push(curBlock)
|
||||
curBlock = B
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/EndBlock()
|
||||
if(curBlock == global_block)
|
||||
return 0
|
||||
|
||||
curBlock = blocks.Pop()
|
||||
return 1
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseAssignment()
|
||||
var/name = curToken.value
|
||||
if(!options.IsValidID(name))
|
||||
errors += new/datum/scriptError/InvalidID(curToken)
|
||||
return
|
||||
|
||||
NextToken()
|
||||
var/t = options.binary_operators[options.assign_operators[curToken.value]]
|
||||
var/datum/node/statement/VariableAssignment/stmt = new()
|
||||
stmt.var_name = new(name)
|
||||
NextToken()
|
||||
|
||||
if(t)
|
||||
stmt.value = new t()
|
||||
stmt.value:exp = new/datum/node/expression/value/variable(stmt.var_name)
|
||||
stmt.value:exp2 = ParseExpression()
|
||||
else
|
||||
stmt.value = ParseExpression()
|
||||
|
||||
curBlock.statements += stmt
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseFunctionStatement()
|
||||
if(!istype(curToken, /datum/token/word))
|
||||
errors += new/datum/scriptError("Bad identifier in function call.")
|
||||
return
|
||||
|
||||
var/datum/node/statement/FunctionCall/stmt=new
|
||||
stmt.func_name = curToken.value
|
||||
NextToken() //skip function name
|
||||
if(!CheckToken("(", /datum/token/symbol)) //Check for and skip open parenthesis
|
||||
return
|
||||
var/loops = 0
|
||||
for()
|
||||
loops++
|
||||
if(loops >= 800)
|
||||
errors +=new/datum/scriptError("Cannot find ending params.")
|
||||
return
|
||||
|
||||
if(!curToken)
|
||||
errors+=new/datum/scriptError/EndOfFile()
|
||||
return
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
||||
curBlock.statements += stmt
|
||||
NextToken() //Skip close parenthesis
|
||||
return
|
||||
|
||||
var/datum/node/expression/P = ParseParamExpression(check_functions = 1)
|
||||
stmt.parameters += P
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ",")
|
||||
NextToken()
|
||||
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
File: Scanner
|
||||
*/
|
||||
/*
|
||||
Class: n_Scanner
|
||||
An object responsible for breaking up source code into tokens for use by the parser.
|
||||
*/
|
||||
/datum/n_Scanner
|
||||
var/list/code
|
||||
/*
|
||||
Var: errors
|
||||
A list of fatal errors found by the scanner. If there are any items in this list, then it is not safe to parse the returned tokens.
|
||||
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the source code found by the scanner.
|
||||
*/
|
||||
var/list/warnings = new
|
||||
|
||||
/*
|
||||
Proc: LoadCode
|
||||
Loads source code.
|
||||
*/
|
||||
/datum/n_Scanner/proc/LoadCode(var/list/c)
|
||||
code=c
|
||||
|
||||
/*
|
||||
Proc: LoadCodeFromFile
|
||||
Gets the code from a file and calls <LoadCode()>.
|
||||
*/
|
||||
/datum/n_Scanner/proc/LoadCodeFromFile(var/f)
|
||||
LoadCode(file2text(f))
|
||||
|
||||
/*
|
||||
Proc: Scan
|
||||
Runs the scanner and returns the resulting list of tokens. Ensure that <LoadCode()> has been called first.
|
||||
*/
|
||||
/datum/n_Scanner/proc/Scan()
|
||||
|
||||
/*
|
||||
Class: nS_Scanner
|
||||
A scanner implementation for n_Script.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner
|
||||
/*
|
||||
Variable: codepos
|
||||
The scanner's position in the source code.
|
||||
*/
|
||||
var/codepos = 1
|
||||
var/line = 1
|
||||
var/linepos = 0 //column=codepos-linepos
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
|
||||
var/commenting = 0
|
||||
// 1: single-line
|
||||
// 2: multi-line
|
||||
/*
|
||||
Variable: ignore
|
||||
A list of characters that are ignored by the scanner.
|
||||
|
||||
Default Value:
|
||||
Whitespace
|
||||
*/
|
||||
var/list/ignore = list(" ", "\t", "\n") //Don't add tokens for whitespace
|
||||
/*
|
||||
Variable: end_stmt
|
||||
A list of characters that end a statement. Each item may only be one character long.
|
||||
|
||||
Default Value:
|
||||
Semicolon
|
||||
*/
|
||||
var/list/end_stmt = list(";")
|
||||
/*
|
||||
Variable: string_delim
|
||||
A list of characters that can start and end strings.
|
||||
|
||||
Default Value:
|
||||
Double and single quotes.
|
||||
*/
|
||||
var/list/string_delim = list("\"", "'")
|
||||
/*
|
||||
Variable: delim
|
||||
A list of characters that denote the start of a new token. This list is automatically populated.
|
||||
*/
|
||||
var/list/delim = new
|
||||
|
||||
/*
|
||||
Macro: COL
|
||||
The current column number.
|
||||
*/
|
||||
#define COL codepos-linepos
|
||||
|
||||
/*
|
||||
Constructor: New
|
||||
Parameters:
|
||||
code - The source code to tokenize.
|
||||
options - An <nS_Options> object used to configure the scanner.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/New(var/list/c, var/datum/n_scriptOptions/nS_Options/options)
|
||||
. = ..()
|
||||
ignore += ascii2text(13) //Carriage return
|
||||
delim += ignore + options.symbols + end_stmt + string_delim
|
||||
src.options = options
|
||||
LoadCode(c)
|
||||
|
||||
/datum/n_Scanner/nS_Scanner/Scan() //Creates a list of tokens from source code
|
||||
var/list/tokens = new
|
||||
for(, src.codepos <= code.len, src.codepos++)
|
||||
var/char = code[codepos]
|
||||
var/nextchar = TCOMMS_SAFE_INDEX(code, codepos + 1)
|
||||
if(char == "\n")
|
||||
line++
|
||||
linepos = codepos
|
||||
|
||||
if(char in ignore)
|
||||
continue
|
||||
|
||||
else if(char == "/" && (nextchar == "*" || nextchar == "/"))
|
||||
ReadComment()
|
||||
|
||||
else if(end_stmt.Find(char))
|
||||
tokens += new/datum/token/end(char, line, COL)
|
||||
|
||||
else if(string_delim.Find(char))
|
||||
codepos++ //skip string delimiter
|
||||
tokens += ReadString(char)
|
||||
|
||||
else if(options.CanStartID(char))
|
||||
tokens += ReadWord()
|
||||
|
||||
else if(options.IsDigit(char))
|
||||
tokens += ReadNumber()
|
||||
|
||||
else if(options.symbols.Find(char))
|
||||
tokens += ReadSymbol()
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
codepos = initial(codepos)
|
||||
line = initial(line)
|
||||
linepos = initial(linepos)
|
||||
return tokens
|
||||
|
||||
|
||||
/*
|
||||
Proc: ReadString
|
||||
Reads a string in the source code into a token.
|
||||
|
||||
Parameters:
|
||||
start - The character used to start the string.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadString(start)
|
||||
var/buf
|
||||
for(, codepos <= code.len, codepos++)//codepos to length(code))
|
||||
var/char = code[codepos]
|
||||
switch(char)
|
||||
if("\\") //Backslash (\) encountered in string
|
||||
codepos++ //Skip next character in string, since it was escaped by a backslash
|
||||
char = TCOMMS_SAFE_INDEX(code, codepos)
|
||||
switch(char)
|
||||
if("\\") //Double backslash
|
||||
buf += "\\"
|
||||
if("n") //\n Newline
|
||||
buf += "\n"
|
||||
else
|
||||
if(char == start) //\" Doublequote
|
||||
buf += start
|
||||
else //Unknown escaped text
|
||||
buf += char
|
||||
if("\n")
|
||||
. = new/datum/token/string(buf, line, COL)
|
||||
errors += new/datum/scriptError("Unterminated string. Newline reached.", .)
|
||||
line++
|
||||
linepos = codepos
|
||||
break
|
||||
else
|
||||
if(char == start) //string delimiter found, end string
|
||||
break
|
||||
else
|
||||
buf += char //Just a normal character in a string
|
||||
if(!.)
|
||||
return new/datum/token/string(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadWord
|
||||
Reads characters separated by an item in <delim> into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadWord()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
|
||||
while(!delim.Find(char))
|
||||
buf += char
|
||||
if(++codepos > code.len) break
|
||||
char = code[codepos]
|
||||
|
||||
codepos-- //allow main Scan() proc to read the delimiter
|
||||
if(options.keywords.Find(buf))
|
||||
return new/datum/token/keyword(buf, line, COL)
|
||||
else
|
||||
return new/datum/token/word(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadSymbol
|
||||
Reads a symbol into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadSymbol()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
|
||||
while(options.symbols.Find(buf + char))
|
||||
buf += char
|
||||
if(++codepos > code.len) break
|
||||
char = code[codepos]
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return new /datum/token/symbol(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadNumber
|
||||
Reads a number into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadNumber()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
var/dec = 0
|
||||
|
||||
while(options.IsDigit(char) || (char == "." && !dec))
|
||||
if(char == ".")
|
||||
dec = 1
|
||||
|
||||
buf += char
|
||||
codepos++
|
||||
char = TCOMMS_SAFE_INDEX(code, codepos)
|
||||
|
||||
var/datum/token/number/T = new(buf, line, COL)
|
||||
if(isnull(text2num(buf)))
|
||||
errors += new/datum/scriptError("Bad number: ", T)
|
||||
T.value = 0
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return T
|
||||
|
||||
/*
|
||||
Proc: ReadComment
|
||||
Reads a comment and outputs the type of comment
|
||||
*/
|
||||
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadComment()
|
||||
var/char = code[codepos]
|
||||
var/nextchar = TCOMMS_SAFE_INDEX(code, codepos + 1)
|
||||
var/charstring = char + nextchar
|
||||
var/comm = 1
|
||||
// 1: single-line comment
|
||||
// 2: multi-line comment
|
||||
var/expectedend = 0
|
||||
|
||||
if(charstring == "//" || charstring == "/*")
|
||||
if(charstring == "/*")
|
||||
comm = 2 // starts a multi-line comment
|
||||
|
||||
while(comm)
|
||||
if(++codepos > code.len)
|
||||
break
|
||||
|
||||
if(expectedend) // ending statement expected...
|
||||
char = code[codepos]
|
||||
if(char == "/") // ending statement found - beak the comment
|
||||
comm = 0
|
||||
break
|
||||
|
||||
if(comm == 2)
|
||||
// multi-line comments are broken by ending statements
|
||||
char = code[codepos]
|
||||
if(char == "*")
|
||||
expectedend = 1
|
||||
continue
|
||||
else
|
||||
char = code[codepos]
|
||||
if(char == "\n")
|
||||
comm = 0
|
||||
break
|
||||
|
||||
if(expectedend)
|
||||
expectedend = 0
|
||||
|
||||
if(comm == 2)
|
||||
errors += new/datum/scriptError/UnterminatedComment()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Class: Token
|
||||
Represents an entity and position in the source code.
|
||||
*/
|
||||
/datum/token
|
||||
var/value
|
||||
var/line
|
||||
var/column
|
||||
|
||||
/datum/token/New(v, l = 0, c = 0)
|
||||
value = v
|
||||
line = l
|
||||
column = c
|
||||
|
||||
/datum/token/string
|
||||
|
||||
/datum/token/symbol
|
||||
|
||||
/datum/token/word
|
||||
|
||||
/datum/token/keyword
|
||||
|
||||
/datum/token/number/New()
|
||||
. = ..()
|
||||
if(!isnum(value))
|
||||
value = text2num(value)
|
||||
ASSERT(!isnull(value))
|
||||
|
||||
/datum/token/accessor
|
||||
var/object
|
||||
var/member
|
||||
|
||||
/datum/token/accessor/New(object, member, l = 0, c = 0)
|
||||
src.object = object
|
||||
src.member = member
|
||||
src.value = "[object].[member]" //for debugging only
|
||||
src.line = l
|
||||
src.column = c
|
||||
|
||||
/datum/token/end
|
||||
@@ -1 +0,0 @@
|
||||
#define TCOMMS_SAFE_INDEX(list, index) list.len > index ? list[index] : null
|
||||
@@ -739,7 +739,7 @@
|
||||
return 1
|
||||
|
||||
/obj/machinery/computer/supplycomp/proc/post_signal(var/command)
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(DISPLAY_FREQ)
|
||||
|
||||
if(!frequency) return
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases
|
||||
|
||||
if(internal_bleeding_rate && !(status_flags & FAKEDEATH))
|
||||
bleed(internal_bleeding_rate)
|
||||
bleed_internal(internal_bleeding_rate)
|
||||
|
||||
if(bleed_rate && !bleedsuppress && !(status_flags & FAKEDEATH))
|
||||
bleed(bleed_rate)
|
||||
@@ -108,6 +108,26 @@
|
||||
if(istype(R) && isturf(loc))
|
||||
R.reaction_turf(get_turf(src), amt * EXOTIC_BLEED_MULTIPLIER)
|
||||
|
||||
/mob/living/carbon/proc/bleed_internal(amt) // Return 1 if we've coughed blood up, 2 if we're vomited it.
|
||||
if(blood_volume)
|
||||
blood_volume = max(blood_volume - amt, 0)
|
||||
if (prob(10 * amt)) // +5% chance per internal bleeding site that we'll cough up blood on a given tick.
|
||||
custom_emote(1, "coughs up blood!")
|
||||
add_splatter_floor(loc, 1)
|
||||
return 1
|
||||
else if (amt >= 1 && prob(5 * amt)) // +2.5% chance per internal bleeding site that we'll cough up blood on a given tick. Must be bleeding internally in more than one place to have a chance at this.
|
||||
vomit(0, 1)
|
||||
return 2
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/bleed_internal(amt)
|
||||
if(!(NO_BLOOD in dna.species.species_traits))
|
||||
.=..()
|
||||
if(dna.species.exotic_blood && .) // Do we have exotic blood, and have we left any on the ground?
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[get_blood_id()]
|
||||
if(istype(R) && isturf(loc))
|
||||
R.reaction_turf(get_turf(src), amt * EXOTIC_BLEED_MULTIPLIER)
|
||||
|
||||
/mob/living/proc/restore_blood()
|
||||
blood_volume = initial(blood_volume)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user