mirror of
https://github.com/goonstation/goonstation-2020.git
synced 2026-08-21 19:16:12 +01:00
751 lines
20 KiB
Plaintext
751 lines
20 KiB
Plaintext
#define CC_ERROR_INVALID_SX 1
|
|
#define CC_ERROR_INVALID_TX 2
|
|
#define CC_ERROR_INVALID_CONTAINER_SX 3
|
|
#define CC_ERROR_INVALID_CONTAINER_TX 4
|
|
#define CC_ERROR_INVALID_TEMPERATURE 5
|
|
#define CC_ERROR_CODE_PROTECTED 6
|
|
#define CC_ERROR_INSTRUCTION_LIMIT 7
|
|
#define CC_NOTIFICATION_COMPLETE 101
|
|
#define CC_NOTIFICATION_SAVED 102
|
|
|
|
#define CC_STATUS_IDLE 1
|
|
#define CC_STATUS_RUNNING 2
|
|
|
|
/**
|
|
* Chemicompiler
|
|
* v1.0 By volundr 9/24/14
|
|
* This device is a programmable chemistry mixing and heating device.
|
|
* The javascript code to run the frontend is in browserassets/js/chemicompiler.min.js
|
|
* which is minified javascript from browserassets/js/chemicompiler.js
|
|
* If you make changes to the uncompressed javascript, you'll need to install node.js,
|
|
* and run `npm install -g uglify-js`
|
|
* then run `uglifyjs browserassets/js/chemicompiler.js -c > browserassets/js/chemicompiler.min.js` to rebuild the compressed version.
|
|
*/
|
|
|
|
/datum/chemicompiler_core
|
|
var/list/buttons[6]
|
|
var/list/cbf[6]
|
|
var/output = ""
|
|
|
|
var/list/currentProg
|
|
var/dp // data pointer
|
|
var/list/data // data
|
|
var/ip // instruction pointer
|
|
|
|
//Registers
|
|
var/sx // Source register
|
|
var/tx // Target register
|
|
var/ax // Amount register
|
|
|
|
var/textBuffer
|
|
var/count
|
|
var/exec
|
|
|
|
var/running = 0
|
|
var/html
|
|
var/datum/tag/page/htmlTag
|
|
var/errorCallback = "err"
|
|
var/transferCallback = "transferReagents"
|
|
var/heatCallback = "heatReagents"
|
|
var/messageCallback = "showMessage"
|
|
var/reservoirClickCallback = "reservoirClick"
|
|
var/reservoirCheckCallback = "reservoirCheck"
|
|
var/topicPermissionCheckCallback = "topicPermissionCheck"
|
|
var/statusChangeCallback
|
|
var/minReservoir = 1
|
|
var/maxReservoir = 10
|
|
var/datum/holder
|
|
var/heading = "don't touch!!!"
|
|
|
|
/datum/chemicompiler_core/New(datum/holder)
|
|
..()
|
|
if(!istype(holder))
|
|
del(src)
|
|
return
|
|
|
|
src.holder = holder
|
|
initHtml()
|
|
|
|
/datum/chemicompiler_core/Topic(href, href_list)
|
|
if(!topicPermissionCheck(href_list["action"]))
|
|
return
|
|
switch(href_list["action"])
|
|
if("getUIState")
|
|
updatePanel()
|
|
|
|
if("reservoir")
|
|
var/resId = text2num(href_list["id"])
|
|
if(resId < minReservoir || resId > maxReservoir)
|
|
return
|
|
|
|
reservoirClick(resId)
|
|
updatePanel()
|
|
|
|
if("loadCode")
|
|
var/buttId = validateButtId(href_list["id"])
|
|
if(!buttId)
|
|
return
|
|
if(buttons[buttId] == 0)
|
|
throwError(CC_ERROR_CODE_PROTECTED)
|
|
output = "E5.NOACC"
|
|
else
|
|
output = html_encode(buttons[buttId])
|
|
|
|
windowCall("loadCodeCallback")
|
|
updatePanel()
|
|
|
|
if("saveCode")
|
|
var/buttId = validateButtId(href_list["id"])
|
|
if(!buttId)
|
|
return
|
|
var/code = href_list["code"]
|
|
buttons[buttId] = code
|
|
cbf[buttId] = parseCBF(code, buttId)
|
|
output = ""
|
|
updatePanel()
|
|
throwError(CC_NOTIFICATION_SAVED) // Saved!
|
|
|
|
windowCall("saveCodeCallback")
|
|
|
|
if("executeCode")
|
|
var/buttId = validateButtId(href_list["id"])
|
|
if(!buttId)
|
|
return
|
|
//var/code = buttons[buttId]
|
|
//boutput(world, "Executing CBF code: '[code]'")
|
|
if(islist(cbf[buttId]))
|
|
runCBF(cbf[buttId])
|
|
|
|
if("reportError")
|
|
var/errorMessage = href_list["message"]
|
|
CRASH("Error reported from chemicompiler frontend: [errorMessage]")
|
|
else
|
|
|
|
/* attack_self(mob/user as mob)
|
|
panel()*/
|
|
|
|
/** Callbacks */
|
|
/datum/chemicompiler_core/proc/topicPermissionCheck(action)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(topicPermissionCheckCallback)
|
|
return call(src.holder, topicPermissionCheckCallback)(action)
|
|
|
|
/datum/chemicompiler_core/proc/showMessage(message)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
message = "<span style=\"color:red\">[message]</span>"
|
|
if(messageCallback)
|
|
return call(src.holder, messageCallback)(message)
|
|
|
|
/datum/chemicompiler_core/proc/throwError(errorCode)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(errorCallback)
|
|
return call(src.holder, errorCallback)(errorCode)
|
|
|
|
/datum/chemicompiler_core/proc/reservoirClick(resId)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(reservoirClickCallback)
|
|
return call(src.holder, reservoirClickCallback)(resId)
|
|
|
|
/datum/chemicompiler_core/proc/reservoirCheck(resId)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if (reservoirCheckCallback)
|
|
return call(src.holder, reservoirCheckCallback)(resId)
|
|
|
|
/datum/chemicompiler_core/proc/transfer(source, target, amount)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if (transferCallback)
|
|
return call(src.holder, transferCallback)(source, target, amount)
|
|
|
|
/datum/chemicompiler_core/proc/heat(res, temp)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if (heatCallback)
|
|
return call(src.holder, heatCallback)(res, temp)
|
|
|
|
/datum/chemicompiler_core/proc/statusChange(oldStatus, newStatus)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
return call(src.holder, "statusChange")(oldStatus, newStatus)
|
|
|
|
/datum/chemicompiler_core/proc/validateButtId(var/id as text)
|
|
var/buttId = text2num(id)
|
|
if(buttId < 1 || buttId > 6)
|
|
return 0
|
|
return buttId
|
|
|
|
/datum/chemicompiler_core/proc/getReservoirStatuses()
|
|
var/list/ret[maxReservoir]
|
|
for(var/i=minReservoir,i<=maxReservoir,i++)
|
|
ret[i] = reservoirCheck(i)
|
|
return ret
|
|
|
|
/datum/chemicompiler_core/proc/windowCall(var/function, var/data = null)
|
|
//boutput(world, "calling window func [function] with data: '[url_encode(data)]'")
|
|
usr << output(data, "chemicompiler.browser:[function]")
|
|
|
|
/datum/chemicompiler_core/proc/updatePanel()
|
|
var json = "{\"reservoirs\":\[null"
|
|
var/i
|
|
var/list/reservoirs = getReservoirStatuses()
|
|
for(i = minReservoir,i<=maxReservoir,i++)
|
|
json += ",[!isnull(reservoirs[i])? "true" : "false"]"
|
|
json += "],\"buttons\":\[null"
|
|
for(i = 1,i <= 6, i++)
|
|
json += ",[!isnull(buttons[i])? "true" : "false"]"
|
|
json += "], \"output\": \"[url_encode(output)]\","
|
|
json += "\"sx\":\"[sx]\","
|
|
json += "\"tx\":\"[tx]\","
|
|
json += "\"ax\":\"[ax]\""
|
|
json += "}"
|
|
windowCall("setUIState", json)
|
|
|
|
/datum/chemicompiler_core/proc/parseCBF(var/string, var/button)
|
|
var/list/tokens = list(">", "<", "+", "-", ".", "\[", "]", "{", "}", "(", ")", "^", "'", "$", "@")
|
|
var/l = lentext(string)
|
|
var/list/inst = new
|
|
var/token
|
|
|
|
for(var/i = 1, i <= l, i++)
|
|
token = copytext(string, i, i+1)
|
|
if(tokens.Find(token))
|
|
inst.Add(token)
|
|
|
|
// ~ means don't allow reading the source code
|
|
if(token == "~")
|
|
buttons[button] = 0
|
|
|
|
. = inst
|
|
|
|
/datum/chemicompiler_core/proc/resetVM()
|
|
dp = 0; // data pointer
|
|
data = list(0) // data
|
|
ip = 1; // instruction pointer
|
|
|
|
//Registers
|
|
sx = 0 // Source register
|
|
tx = 0 // Target register
|
|
ax = 0 // Amount register
|
|
|
|
textBuffer = ""
|
|
count = 0
|
|
exec = 0;
|
|
|
|
/datum/chemicompiler_core/proc/runCBF(var/list/inst)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
resetVM()
|
|
running = 1
|
|
statusChange(CC_STATUS_RUNNING)
|
|
currentProg = inst
|
|
SPAWN_DBG(0)
|
|
resumeCBF()
|
|
|
|
/datum/chemicompiler_core/proc/resumeCBF()
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
while(running)
|
|
if(ip > currentProg.len)
|
|
running = 0
|
|
break
|
|
LAGCHECK(LAG_MED)
|
|
exec++
|
|
if(exec > 50000)
|
|
throwError(CC_ERROR_INSTRUCTION_LIMIT) // Instruction limit reached.
|
|
running = 0
|
|
break
|
|
switch(currentProg[ip++])
|
|
if(">")
|
|
dp++
|
|
if(dp > 1023) // RAM limit is 1024 bytes.
|
|
dp = 0
|
|
if("<")
|
|
dp--
|
|
if(dp < 0) // RAM limit is 1024 bytes.
|
|
dp = 1023
|
|
if("+")
|
|
data[dp + 1]++
|
|
if("-")
|
|
data[dp + 1]--
|
|
if(".")
|
|
textBuffer += ascii2text(data[dp+1])
|
|
//if(",") // disabled input instruction from BF
|
|
if("\[")
|
|
if(data[dp + 1] == 0)
|
|
count = 1
|
|
while(ip <= currentProg.len && count > 0)
|
|
if(currentProg[ip] == "\[")
|
|
count++
|
|
if(currentProg[ip] == "]")
|
|
count--
|
|
ip++
|
|
|
|
if("]")
|
|
if(data[dp + 1] != 0)
|
|
count = 1
|
|
ip--
|
|
while(ip > 1 && count > 0)
|
|
ip--
|
|
if(currentProg[ip] == "\[")
|
|
count--
|
|
if(currentProg[ip] == "]")
|
|
count++
|
|
if(ip == 1 && count > 0)
|
|
return //failed to find matching brace...
|
|
if("{")
|
|
data[dp + 1] = sx
|
|
if("}")
|
|
sx = data[dp + 1]
|
|
if("(")
|
|
data[dp + 1] = tx
|
|
if(")")
|
|
tx = data[dp + 1]
|
|
if("^")
|
|
data[dp + 1] = ax
|
|
if("'")
|
|
ax = data[dp + 1]
|
|
if("$")
|
|
var/heatTo = (273 - tx) + ax
|
|
heatReagents(sx, heatTo)
|
|
|
|
if("@")
|
|
// Transfer commands delay by 1/4 second per ml.
|
|
transferReagents(sx, tx, ax)
|
|
else
|
|
|
|
if(data.len < dp + 1)
|
|
data.len = dp + 1
|
|
if(lentext(textBuffer) > 80)
|
|
output += "[textBuffer]<br>"
|
|
textBuffer = ""
|
|
updatePanel()
|
|
|
|
if(exec % 100 == 0)
|
|
SPAWN_DBG(0)
|
|
resumeCBF()
|
|
break
|
|
if(!running)
|
|
output += textBuffer
|
|
updatePanel()
|
|
statusChange(CC_STATUS_IDLE)
|
|
throwError(CC_NOTIFICATION_COMPLETE)
|
|
|
|
/datum/chemicompiler_core/proc/transferReagents(var/source, var/target, var/amount)
|
|
if(source < minReservoir || source > maxReservoir)
|
|
throwError(CC_ERROR_INVALID_SX) // Invalid source id.
|
|
return
|
|
if(target < minReservoir || target > maxReservoir + 3)
|
|
throwError(CC_ERROR_INVALID_TX) // Invalid target id.
|
|
return
|
|
if(!reservoirCheck(source))
|
|
throwError(CC_ERROR_INVALID_CONTAINER_SX) // No reservoir loaded in source
|
|
return
|
|
if(target <= maxReservoir && !reservoirCheck(target))
|
|
throwError(CC_ERROR_INVALID_CONTAINER_TX) // No reservoir loaded in target
|
|
return
|
|
|
|
transfer(source, target, amount)
|
|
|
|
/datum/chemicompiler_core/proc/heatReagents(var/rid, var/temp)
|
|
if(rid < minReservoir || rid > maxReservoir)
|
|
throwError(CC_ERROR_INVALID_SX) // Invalid reservoir id
|
|
return
|
|
if(!reservoirCheck(rid))
|
|
throwError(CC_ERROR_INVALID_CONTAINER_SX) // No reservoir loaded in specified position
|
|
return
|
|
if(temp < 1 || temp > 9000) //lol you wacky fuck
|
|
throwError(CC_ERROR_INVALID_TEMPERATURE) // Invalid temperature value
|
|
return
|
|
|
|
heat(rid, temp)
|
|
|
|
/datum/chemicompiler_core/proc/panel()
|
|
set background = 1
|
|
|
|
// HTML is built only once, via New() -- all subsequent updates are done using javascript. Slick.
|
|
usr.Browse(html, "window=chemicompiler;size=420x600")
|
|
|
|
/datum/chemicompiler_core/proc/initHtml()
|
|
set background = 1
|
|
var/i
|
|
htmlTag = new
|
|
|
|
var/datum/tag/script/controlscr = new
|
|
controlscr.setContent( {"
|
|
var ref = '\ref[src]';
|
|
"})
|
|
htmlTag.addToHead(controlscr)
|
|
|
|
var/datum/tag/heading/h = new(1)
|
|
h.setText("ChemiCompiler")
|
|
h.setStyle("color", "#F3F1FC")
|
|
h.setStyle("padding", "10px")
|
|
h.setStyle("border-bottom", "3px solid #243030")
|
|
h.setStyle("border-right", "2px solid #243030")
|
|
htmlTag.addToBody(h)
|
|
|
|
var/datum/tag/div/dataDiv = new
|
|
dataDiv.setId("data")
|
|
htmlTag.addToBody(dataDiv)
|
|
|
|
var/datum/tag/div/codeDiv = new
|
|
codeDiv.setId("code-container")
|
|
htmlTag.addToBody(codeDiv)
|
|
var/datum/tag/textarea/codeInput = new
|
|
codeInput.setId("code-input")
|
|
codeDiv.addChildElement(codeInput)
|
|
|
|
var/datum/tag/div/container = new
|
|
container.addClass("container-fluid")
|
|
htmlTag.addToBody(container)
|
|
var/datum/tag/div/row = new
|
|
row.addClass("row-fluid")
|
|
container.addChildElement(row)
|
|
|
|
var/datum/tag/button/butt_load = new
|
|
var/datum/tag/button/butt_save = new
|
|
butt_load.setText("load")
|
|
butt_save.setText("save")
|
|
butt_load.setId("butt-load")
|
|
butt_save.setId("butt-save")
|
|
butt_load.addClass("btn btn-primary btn-sl")
|
|
butt_save.addClass("btn btn-danger btn-sl")
|
|
row.addChildElement(butt_load)
|
|
row.addChildElement(butt_save)
|
|
|
|
row = new
|
|
row.addClass("row-fluid")
|
|
container.addChildElement(row)
|
|
|
|
// lol butts
|
|
var/datum/tag/button/butt
|
|
for(i=1,i<=6,i++)
|
|
butt = new
|
|
butt.setText(i)
|
|
butt.setId("butt-[i]")
|
|
butt.addClass("btn btn-default btn-c")
|
|
butt.setAttribute("data-btn-id", i)
|
|
row.addChildElement(butt)
|
|
|
|
row = new
|
|
row.addClass("row-fluid")
|
|
container.addChildElement(row)
|
|
var/datum/tag/button/resBtn
|
|
for(i=minReservoir,i<=maxReservoir,i++)
|
|
resBtn = new
|
|
resBtn.setText("r[i]")
|
|
resBtn.setId("reservoir-[i]")
|
|
resBtn.addClass("btn reservoir-button")
|
|
resBtn.setAttribute("data-id", i)
|
|
row.addChildElement(resBtn)
|
|
|
|
if(i % 5 == 0)
|
|
row = new
|
|
row.addClass("row-fluid")
|
|
container.addChildElement(row)
|
|
|
|
row = new
|
|
row.addClass("row-fluid")
|
|
container.addChildElement(row)
|
|
var/datum/tag/label/sxLabel = new
|
|
var/datum/tag/label/txLabel = new
|
|
var/datum/tag/label/axLabel = new
|
|
sxLabel.setText("sx ")
|
|
txLabel.setText("tx ")
|
|
axLabel.setText("ax ")
|
|
var/datum/tag/input/sx = new
|
|
var/datum/tag/input/tx = new
|
|
var/datum/tag/input/ax = new
|
|
sxLabel.addChildElement(sx)
|
|
txLabel.addChildElement(tx)
|
|
axLabel.addChildElement(ax)
|
|
sx.setId("sx-input")
|
|
tx.setId("tx-input")
|
|
ax.setId("ax-input")
|
|
sx.setAttribute("onfocus", "$(this).blur()")
|
|
tx.setAttribute("onfocus", "$(this).blur()")
|
|
ax.setAttribute("onfocus", "$(this).blur()")
|
|
row.addChildElement(sxLabel)
|
|
row.addChildElement(txLabel)
|
|
row.addChildElement(axLabel)
|
|
|
|
var/datum/tag/cssinclude/bootstrap = new
|
|
bootstrap.setHref(resource("css/bootstrap.min.css"))
|
|
htmlTag.addToHead(bootstrap)
|
|
|
|
var/datum/tag/cssinclude/chemicss = new
|
|
chemicss.setHref(resource("css/chemicompiler.css"))
|
|
htmlTag.addToHead(chemicss)
|
|
|
|
var/datum/tag/scriptinclude/json2 = new
|
|
json2.setSrc(resource("js/json2.min.js"))
|
|
htmlTag.addToHead(json2)
|
|
|
|
var/datum/tag/scriptinclude/jquery = new
|
|
jquery.setSrc(resource("js/jquery.min.js"))
|
|
htmlTag.addToHead(jquery)
|
|
|
|
var/datum/tag/scriptinclude/jqueryMigrate = new
|
|
jqueryMigrate.setSrc(resource("js/jquery.migrate.js"))
|
|
htmlTag.addToHead(jqueryMigrate)
|
|
|
|
var/datum/tag/scriptinclude/bootstrapJs = new
|
|
bootstrapJs.setSrc(resource("js/bootstrap.min.js"))
|
|
htmlTag.addToBody(bootstrapJs)
|
|
|
|
var/datum/tag/scriptinclude/chemicompilerJs = new
|
|
chemicompilerJs.setSrc(resource("js/chemicompiler.js"))
|
|
htmlTag.addToBody(chemicompilerJs)
|
|
|
|
//var/datum/tag/firebug/fb = new
|
|
//htmlTag.addToBody(fb)
|
|
|
|
html = htmlTag.toHtml()
|
|
|
|
/datum/chemicompiler_core/testCore
|
|
errorCallback = "err"
|
|
transferCallback = "transfer"
|
|
heatCallback = "heat"
|
|
messageCallback = "msg"
|
|
reservoirClickCallback = "reservoirClick"
|
|
reservoirCheckCallback = "reservoirCheck"
|
|
topicPermissionCheckCallback = "topicPermissionCheck"
|
|
|
|
/datum/testChemicompilerHolder
|
|
var/datum/chemicompiler_core/cc
|
|
|
|
New()
|
|
..()
|
|
cc = new /datum/chemicompiler_core/testCore(src)
|
|
proc
|
|
err(errorCode)
|
|
var/errorMessage = interpretError(errorCode)
|
|
boutput(world, "Chemicompiler error: [errorCode]; [errorMessage]")
|
|
interpretError(errorCode)
|
|
switch(errorCode)
|
|
if(CC_ERROR_INVALID_SX)
|
|
return "Error: invalid source reservor id."
|
|
if(CC_ERROR_INVALID_TX)
|
|
return "Error: invalid target reservoir id."
|
|
if(CC_ERROR_INVALID_CONTAINER_SX)
|
|
return "Error: invalid or missing container in source reservoir slot."
|
|
if(CC_ERROR_INVALID_CONTAINER_TX)
|
|
return "Error: invalid or missing container in target reservoir slot."
|
|
if(CC_ERROR_INVALID_TEMPERATURE)
|
|
return "Error: Invalid temperature passed to heat proc."
|
|
if(CC_ERROR_CODE_PROTECTED)
|
|
return "Error: code protected - cannot retrieve."
|
|
if(CC_ERROR_INSTRUCTION_LIMIT)
|
|
return "Error: instruction limit reached."
|
|
if(CC_NOTIFICATION_COMPLETE)
|
|
return "Notification: program complete."
|
|
if(CC_NOTIFICATION_SAVED)
|
|
return "Notification: code saved."
|
|
else
|
|
return "Unknown error code: [errorCode]"
|
|
|
|
transfer(source, target, amount)
|
|
boutput(world, "Chemicompiler transfer [amount] units from [source] to [target]")
|
|
heat(rid, temp)
|
|
boutput(world, "Chemicompiler heat [rid] to [temp]K")
|
|
msg(message)
|
|
boutput(world, message)
|
|
reservoirClick(rid)
|
|
boutput(world, "Clicked reservoir #[rid]")
|
|
reservoirCheck(rid)
|
|
boutput(world, "Checked existence for reservoir #[rid]")
|
|
return 1
|
|
topicPermissionCheck(action)
|
|
boutput(world, "Topic permission check for \ref[usr], [action]")
|
|
return 1
|
|
|
|
/datum/chemicompiler_executor
|
|
var/list/reservoirs = list()
|
|
var/datum/holder
|
|
var/datum/chemicompiler_core/core
|
|
|
|
/datum/chemicompiler_executor/New(datum/holder, corePath = /datum/chemicompiler_core/portableCore)
|
|
..()
|
|
if(!istype(holder))
|
|
del(src)
|
|
return
|
|
|
|
src.holder = holder
|
|
src.core = new corePath(src)
|
|
src.reservoirs.len = src.core.maxReservoir
|
|
|
|
for(var/i=src.core.minReservoir,i<=src.core.maxReservoir,i++)
|
|
reservoirs[i] = null
|
|
|
|
/datum/chemicompiler_executor/proc/panel()
|
|
core.panel()
|
|
|
|
/datum/chemicompiler_executor/proc/err(errorCode)
|
|
switch(errorCode)
|
|
if(CC_ERROR_INVALID_SX)
|
|
beepCode(1, 1)
|
|
if(CC_ERROR_INVALID_TX)
|
|
beepCode(1, 1)
|
|
if(CC_ERROR_INVALID_CONTAINER_SX)
|
|
beepCode(3, 1)
|
|
if(CC_ERROR_INVALID_CONTAINER_TX)
|
|
beepCode(3, 1)
|
|
if(CC_ERROR_INVALID_TEMPERATURE)
|
|
beepCode(4, 1)
|
|
if(CC_ERROR_CODE_PROTECTED)
|
|
beepCode(5, 1)
|
|
if(CC_ERROR_INSTRUCTION_LIMIT)
|
|
beepCode(2, 1)
|
|
if(CC_NOTIFICATION_COMPLETE)
|
|
beepCode(1)
|
|
if(CC_NOTIFICATION_SAVED)
|
|
beepCode(2)
|
|
else
|
|
|
|
/datum/chemicompiler_executor/proc/reservoirCheck(resId)
|
|
if(istype(reservoirs[resId], /obj/item/reagent_containers/glass))
|
|
return 1
|
|
/datum/chemicompiler_executor/proc/reservoirClick(resId)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(istype(reservoirs[resId], /obj/item/reagent_containers/glass))
|
|
// Taking a res out
|
|
if(!usr.equipped())
|
|
boutput(usr, "<span style=\"color:blue\">You remove the [reservoirs[resId]] from the [src.holder].</span>")
|
|
usr.put_in_hand_or_drop(reservoirs[resId])
|
|
reservoirs[resId] = null
|
|
else
|
|
var/obj/item/reagent_containers/glass/B = reservoirs[resId]
|
|
B.attackby(usr.equipped(), usr)
|
|
|
|
else
|
|
// Putting SOMETHING in
|
|
var/obj/item/I = usr.equipped()
|
|
if(istype(I, /obj/item/reagent_containers/glass))
|
|
//putting a reagent container in
|
|
boutput(usr, "<span style=\"color:blue\">You place the [I] into the [src.holder].</span>")
|
|
usr.drop_item()
|
|
I.set_loc(holder)
|
|
reservoirs[resId] = I
|
|
|
|
/datum/chemicompiler_executor/proc/topicPermissionCheck(action)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
return call(src.holder, "topicPermissionCheck")(action)
|
|
|
|
/**
|
|
* beep codes
|
|
* loud:
|
|
* 1: Invalid sx or tx value
|
|
* 2: Instruction limit reached
|
|
* 3: No container loaded at source or target
|
|
* 4: Invalid temperature value
|
|
* 5: Code is protected, cannot load
|
|
* soft:
|
|
* 1: done executing
|
|
* 2: code saved
|
|
*/
|
|
/datum/chemicompiler_executor/proc/beepCode(var/number = 1, var/loud = 0)
|
|
var/how = loud ? "loudly" : "quietly"
|
|
var/howmany = number == 1 ? "once" : "[number] times"
|
|
var/message = "[src.holder] beeps [how] [howmany]."
|
|
showMessage(message)
|
|
|
|
/datum/chemicompiler_executor/proc/showMessage(message)
|
|
if(!istype(holder))
|
|
del(src)
|
|
return
|
|
message = "<span style=\"color:red\">[message]</span>"
|
|
if(istype(holder:loc, /mob))
|
|
boutput(holder:loc, message)
|
|
else
|
|
holder:visible_message(message)
|
|
|
|
/datum/chemicompiler_executor/proc/transferReagents(var/source, var/target, var/amount)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(source < 1 || source > 10 || target < 1 || target > 13)
|
|
beepCode(1, 1) // Invalid source or target id.
|
|
return
|
|
if(!istype(reservoirs[source], /obj/item/reagent_containers/glass))
|
|
beepCode(3, 1) // No reservoir loaded in source or target
|
|
return
|
|
if(target < 11 && !istype(reservoirs[target], /obj/item/reagent_containers/glass))
|
|
beepCode(3, 1) // No reservoir loaded in source or target
|
|
return
|
|
|
|
// Transfer commands delay by 1/2 second per ml.
|
|
showMessage("[src.holder] emits a slight humming sound.")
|
|
sleep(round(amount * 2.5))
|
|
|
|
var/holder = reservoirs[source]
|
|
var/datum/reagents/RS = holder:reagents
|
|
|
|
if(target < 11)
|
|
var/obj/RT = reservoirs[target]
|
|
RS.trans_to(RT, amount)
|
|
if (target == 11)
|
|
// Generate pill
|
|
showMessage("[src.holder] makes an alarming grinding noise!")
|
|
sleep(10)
|
|
var/obj/item/reagent_containers/pill/P = new(get_turf(src.holder))
|
|
RS.trans_to(P, amount)
|
|
showMessage("[src.holder] ejects a pill.")
|
|
if (target == 12)
|
|
// Generate vial
|
|
var/obj/item/reagent_containers/glass/vial/V = new(get_turf(src.holder))
|
|
RS.trans_to(V, amount)
|
|
showMessage("[src.holder] ejects a vial of some unknown substance.")
|
|
if (target == 13)
|
|
RS.trans_to(get_turf(src.holder), amount)
|
|
showMessage("Something drips out the side of [src.holder].")
|
|
sleep(10)
|
|
|
|
/datum/chemicompiler_executor/proc/heatReagents(var/rid, var/temp)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if(rid < 1 || rid > 10)
|
|
beepCode(1, 1) // Invalid reservoir id
|
|
return
|
|
if(!istype(reservoirs[rid], /obj/item/reagent_containers/glass))
|
|
beepCode(3, 1) // No reservoir loaded in specified position
|
|
return
|
|
if(temp < 1 || temp > 9000) //lol you wacky fuck
|
|
beepCode(4, 1) // Invalid temperature value
|
|
return
|
|
|
|
// Ok now heat this bitch
|
|
showMessage("[src.holder] clicks.") // Relay kicking on
|
|
var/holder = reservoirs[rid]
|
|
var/datum/reagents/R = holder:reagents
|
|
while(R.total_volume && abs(R.total_temperature - temp) > 3)
|
|
R.temperature_reagents(temp, 10)
|
|
sleep(10)
|
|
|
|
showMessage("[src.holder] clicks.") // Relay kicking off
|
|
|
|
/datum/chemicompiler_executor/proc/statusChange(oldStatus, newStatus)
|
|
if(!istype(src.holder))
|
|
del(src)
|
|
return
|
|
if (core.statusChangeCallback)
|
|
return call(holder, core.statusChangeCallback)(oldStatus, newStatus) |