fixes merge conflict with floorcluwne/admin topic.dm

This commit is contained in:
Kyep
2018-11-16 23:08:09 -08:00
139 changed files with 35105 additions and 4242 deletions
+8 -2
View File
@@ -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
+27 -9
View File
@@ -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)
+7
View File
@@ -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"
+2 -1
View File
@@ -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
View File
@@ -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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(role, " ", "&nbsp")]</font></a></td>"
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'>[replacetext(role, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(role, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'><font color=red>[replacetext(role, " ", "&nbsp")]</font></a></td>"
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()]'>[replacetext(role, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[role];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(role, " ", "&nbsp")]</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, " ", "&nbsp")]</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, " ", "&nbsp")]</font></a></td>"
counter++
else
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()]'>[replacetext(job.title, " ", "&nbsp")]</a></td>"
jobs += "<td width='20%'><a href='?src=[UID()];jobban3=[job.title];jobban4=[M.UID()];dbbanaddckey=[M.ckey]'>[replacetext(job.title, " ", "&nbsp")]</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"])
+16 -14
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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()
+21 -31
View File
@@ -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.
+2 -2
View File
@@ -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()
+18
View File
@@ -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()
..()
+2 -1
View File
@@ -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)
)
+15
View File
@@ -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
View File
@@ -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"
+1 -1
View File
@@ -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]")
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+11 -3
View File
@@ -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
+2 -11
View File
@@ -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"
-129
View File
@@ -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])"
-45
View File
@@ -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 ..()
-110
View File
@@ -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
-169
View File
@@ -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()
. = ..()
-122
View File
@@ -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)
-204
View File
@@ -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
-205
View File
@@ -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()
-293
View File
@@ -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()
-40
View File
@@ -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
View File
@@ -1 +0,0 @@
#define TCOMMS_SAFE_INDEX(list, index) list.len > index ? list[index] : null
+1 -1
View File
@@ -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
+21 -1
View File
@@ -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)