mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-09-26 23:54:57 +00:00
Map scripts now being built as PLASMA source which is then compiled, instead of direct-to-bytecode. This will be much easier to maintain once it's fully working.
This commit is contained in:
parent
c0e98cd6c4
commit
042450d4ce
@ -906,13 +906,11 @@ class PackPartitions
|
|||||||
def name = "mapScript$num"
|
def name = "mapScript$num"
|
||||||
//println "Packing scripts for map $mapName, to module $num."
|
//println "Packing scripts for map $mapName, to module $num."
|
||||||
|
|
||||||
|
def scriptDir = "build/"
|
||||||
ScriptModule module = new ScriptModule()
|
ScriptModule module = new ScriptModule()
|
||||||
module.packScripts(mapName, mapEl.scripts ? mapEl.scripts[0] : [],
|
module.packScripts(mapName, new File(new File(scriptDir), name+".pla"), mapEl.scripts ? mapEl.scripts[0] : [],
|
||||||
totalWidth, totalHeight, xRange, yRange)
|
totalWidth, totalHeight, xRange, yRange)
|
||||||
|
compileModule(name, scriptDir)
|
||||||
modules[name] = [num:num, buf:wrapByteList(module.data)]
|
|
||||||
bytecodes[name] = [num:num, buf:wrapByteList(module.bytecode)]
|
|
||||||
fixups[name] = [num:num, buf:wrapByteList(module.fixups)]
|
|
||||||
return [num, module.locationsWithTriggers]
|
return [num, module.locationsWithTriggers]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1890,55 +1888,32 @@ class PackPartitions
|
|||||||
|
|
||||||
class ScriptModule
|
class ScriptModule
|
||||||
{
|
{
|
||||||
def data = []
|
PrintWriter out
|
||||||
def bytecode = []
|
|
||||||
def fixups = []
|
|
||||||
|
|
||||||
def nScripts = 0
|
|
||||||
def nStringBytes = 0
|
|
||||||
|
|
||||||
def locationsWithTriggers = [] as Set
|
def locationsWithTriggers = [] as Set
|
||||||
|
def scriptNames = [:]
|
||||||
|
def indent = 0
|
||||||
|
|
||||||
def vec_setScriptInfo = 0x1F00
|
def emitString(inStr)
|
||||||
def vec_pushAuxStr = 0x1F03
|
|
||||||
def vec_displayStr = 0x1F06
|
|
||||||
def vec_displayStrNL = 0x1F09
|
|
||||||
def vec_getYN = 0x1F0C
|
|
||||||
def vec_setMap = 0x1F0F
|
|
||||||
def vec_setSky = 0x1F12
|
|
||||||
def vec_setGround = 0x1F15
|
|
||||||
def vec_teleport = 0x1F18
|
|
||||||
def vec_setPortrait = 0x1F1B
|
|
||||||
def vec_clrPortrait = 0x1F1E
|
|
||||||
def vec_moveBackward = 0x1F21
|
|
||||||
def vec_getCharacter = 0x1F24
|
|
||||||
def vec_clrTextWindow = 0x1F27
|
|
||||||
|
|
||||||
def emitAuxString(inStr)
|
|
||||||
{
|
{
|
||||||
emitCodeByte(0x54) // CALL
|
out << '\"'
|
||||||
emitCodeWord(vec_pushAuxStr)
|
def prev = '\0'
|
||||||
def buf = new StringBuilder()
|
|
||||||
def prev = ' '
|
|
||||||
inStr.each { ch ->
|
inStr.each { ch ->
|
||||||
if (ch == '^') {
|
if (ch == '^') {
|
||||||
if (prev == '^')
|
if (prev == '^')
|
||||||
buf.append(ch)
|
out << ch
|
||||||
}
|
}
|
||||||
|
else if (ch == '\"')
|
||||||
|
out << "\\\""
|
||||||
else if (prev == '^') {
|
else if (prev == '^') {
|
||||||
def cp = Character.codePointAt(ch.toUpperCase(), 0)
|
def cp = Character.codePointAt(ch.toUpperCase(), 0)
|
||||||
if (cp > 64 && cp < 96)
|
if (cp > 64 && cp < 96)
|
||||||
buf.appendCodePoint(cp - 64)
|
out << "\\\$" << String.format("%02X", cp - 64)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
buf.append(ch)
|
out << ch
|
||||||
prev = ch
|
prev = ch
|
||||||
}
|
}
|
||||||
def str = buf.toString()
|
out << '\"'
|
||||||
assert str.size() < 256 : "String too long, max is 255 characters: $str"
|
|
||||||
emitCodeByte(str.size())
|
|
||||||
str.each { ch -> emitCodeByte((byte)ch) }
|
|
||||||
nStringBytes += str.size() + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def getScriptName(script)
|
def getScriptName(script)
|
||||||
@ -1947,8 +1922,11 @@ class PackPartitions
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
def blk = script.block[0]
|
def blk = script.block[0]
|
||||||
if (blk.field.size() == 0)
|
if (blk.field.size() == 0) {
|
||||||
|
if (scriptNames.containsKey(script))
|
||||||
|
return scriptNames[script]
|
||||||
return null
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
assert blk.field[0].@name == "NAME"
|
assert blk.field[0].@name == "NAME"
|
||||||
return blk.field[0].text()
|
return blk.field[0].text()
|
||||||
@ -1958,72 +1936,59 @@ class PackPartitions
|
|||||||
* Pack scripts from a map. Either the whole map, or optionally just an X and Y
|
* Pack scripts from a map. Either the whole map, or optionally just an X and Y
|
||||||
* bounded section of it.
|
* bounded section of it.
|
||||||
*/
|
*/
|
||||||
def packScripts(mapName, inScripts, maxX, maxY, xRange = null, yRange = null)
|
def packScripts(mapName, outFile, inScripts, maxX, maxY, xRange = null, yRange = null)
|
||||||
{
|
{
|
||||||
// If we're only processing a section of the map, make sure this script is
|
out = new PrintWriter(new FileWriter(outFile))
|
||||||
// referenced within that section.
|
out << "// Generated code - DO NOT MODIFY BY HAND\n\n"
|
||||||
//
|
out << "include \"../src/plasma/gamelib.plh\"\n"
|
||||||
|
out << "include \"../src/plasma/playtype.plh\"\n"
|
||||||
|
out << "include \"../src/plasma/gen_images.plh\"\n\n"
|
||||||
|
|
||||||
|
// Determine which scripts are referenced in the specified section of the map.
|
||||||
|
def initScript
|
||||||
def scripts = []
|
def scripts = []
|
||||||
inScripts.script.eachWithIndex { script, idx ->
|
inScripts.script.eachWithIndex { script, idx ->
|
||||||
def name = getScriptName(script)
|
def name = getScriptName(script)
|
||||||
if (name != null && name.toLowerCase() == "init")
|
if (name != null && name.toLowerCase() == "init") {
|
||||||
scripts << script
|
initScript = script
|
||||||
|
}
|
||||||
else if (script.locationTrigger.any { trig ->
|
else if (script.locationTrigger.any { trig ->
|
||||||
(!xRange || trig.@x.toInteger() in xRange) &&
|
(!xRange || trig.@x.toInteger() in xRange) &&
|
||||||
(!yRange || trig.@y.toInteger() in yRange) })
|
(!yRange || trig.@y.toInteger() in yRange) })
|
||||||
|
{
|
||||||
scripts << script
|
scripts << script
|
||||||
|
scriptNames[script] = "trig_$idx"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nScripts = scripts.script.size()
|
|
||||||
|
|
||||||
// Even if there were no scripts, we still need an init to display
|
// Even if there were no scripts, we still need an init to display
|
||||||
// the map name.
|
// the map name.
|
||||||
makeStubs()
|
makeTriggerTbl(scripts, xRange, yRange)
|
||||||
scripts.eachWithIndex { script, idx ->
|
scripts.eachWithIndex { script, idx ->
|
||||||
packScript(idx, script)
|
packScript(idx, script)
|
||||||
}
|
}
|
||||||
makeInit(mapName, scripts, xRange, yRange, maxX, maxY)
|
makeInit(mapName, initScript, maxX, maxY)
|
||||||
emitFixupByte(0xFF)
|
|
||||||
|
|
||||||
//println " Code stats: data=${data.size}, bytecode=${bytecode.size} (str=$nStringBytes), fixups=${fixups.size}"
|
out.close()
|
||||||
//println "data: $data"
|
|
||||||
//println "bytecode: $bytecode"
|
|
||||||
//println "fixups: $fixups"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeStubs()
|
def outIndented(str) {
|
||||||
{
|
out << (" " * indent) << str
|
||||||
// Emit a stub for each function, including the init function
|
|
||||||
(0..nScripts).each { it ->
|
|
||||||
emitDataByte(0x20) // JSR
|
|
||||||
emitDataWord(0x3DC) // Aux mem interp ($3DC)
|
|
||||||
emitDataWord(0) // Placeholder for the bytecode offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def startFunc(scriptNum)
|
|
||||||
{
|
|
||||||
def fixAddr = (scriptNum * 5) + 3
|
|
||||||
assert data[fixAddr] == 0 && data[fixAddr+1] == 0
|
|
||||||
data[fixAddr] = (byte)(bytecodeAddr() & 0xFF)
|
|
||||||
data[fixAddr+1] = (byte)((bytecodeAddr() >> 8) & 0xFF)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packScript(scriptNum, script)
|
def packScript(scriptNum, script)
|
||||||
{
|
{
|
||||||
def name = getScriptName(script)
|
|
||||||
if (name.toLowerCase() == "init") // this special script gets processed later
|
|
||||||
return
|
|
||||||
|
|
||||||
//println " Script '$name'"
|
//println " Script '$name'"
|
||||||
withContext("script '$name'")
|
withContext("script $scriptNum")
|
||||||
{
|
{
|
||||||
if (script.block.size() == 0) {
|
if (script.block.size() == 0) {
|
||||||
printWarning("empty script found; skipping.")
|
printWarning("empty script found; skipping.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the function's start address in its corresponding stub
|
// Record the function's name and start its definition
|
||||||
startFunc(scriptNum+1)
|
out << "def script$scriptNum()\n"
|
||||||
|
indent = 1
|
||||||
|
|
||||||
// Process the code inside it
|
// Process the code inside it
|
||||||
def proc = script.block[0]
|
def proc = script.block[0]
|
||||||
@ -2038,17 +2003,10 @@ class PackPartitions
|
|||||||
printWarning "empty statement found; skipping."
|
printWarning "empty statement found; skipping."
|
||||||
|
|
||||||
// And complete the function
|
// And complete the function
|
||||||
finishFunc()
|
out << "end\n\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def finishFunc()
|
|
||||||
{
|
|
||||||
// Finish off the function with a return value and return opcode
|
|
||||||
emitCodeByte(0) // ZERO
|
|
||||||
emitCodeByte(0x5C) // RET
|
|
||||||
}
|
|
||||||
|
|
||||||
def packBlock(blk)
|
def packBlock(blk)
|
||||||
{
|
{
|
||||||
withContext("${blk.@type}") {
|
withContext("${blk.@type}") {
|
||||||
@ -2060,7 +2018,7 @@ class PackPartitions
|
|||||||
case 'text_clear_window':
|
case 'text_clear_window':
|
||||||
packClearWindow(blk); break
|
packClearWindow(blk); break
|
||||||
case 'text_getanykey':
|
case 'text_getanykey':
|
||||||
packGetAnyKey(); break
|
packGetAnyKey(blk); break
|
||||||
case 'controls_if':
|
case 'controls_if':
|
||||||
packIfStmt(blk); break
|
packIfStmt(blk); break
|
||||||
case 'events_set_map':
|
case 'events_set_map':
|
||||||
@ -2086,60 +2044,15 @@ class PackPartitions
|
|||||||
blk.next.each { it.block.each { packBlock(it) } }
|
blk.next.each { it.block.each { packBlock(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
def dataAddr()
|
def getSingle(els, name = null, type = null)
|
||||||
{
|
{
|
||||||
return data.size()
|
assert els.size() == 1
|
||||||
}
|
def first = els[0]
|
||||||
|
if (name)
|
||||||
def bytecodeAddr()
|
assert first.@name == name
|
||||||
{
|
if (type)
|
||||||
return bytecode.size()
|
assert first.@type == type
|
||||||
}
|
return first
|
||||||
|
|
||||||
def emitDataByte(b)
|
|
||||||
{
|
|
||||||
assert b <= 255
|
|
||||||
data.add((byte)(b & 0xFF))
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitDataWord(w)
|
|
||||||
{
|
|
||||||
emitDataByte(w & 0xFF)
|
|
||||||
emitDataByte(w >> 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitCodeByte(b)
|
|
||||||
{
|
|
||||||
assert b <= 255
|
|
||||||
bytecode.add((byte)(b & 0xFF))
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitCodeWord(w)
|
|
||||||
{
|
|
||||||
emitCodeByte(w & 0xFF)
|
|
||||||
emitCodeByte(w >> 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitFixupByte(b)
|
|
||||||
{
|
|
||||||
assert b <= 255
|
|
||||||
fixups.add((byte)(b & 0xFF))
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitDataFixup(toAddr)
|
|
||||||
{
|
|
||||||
// Src addr is reversed (i.e. it's hi then lo)
|
|
||||||
emitFixupByte(dataAddr() >> 8)
|
|
||||||
emitFixupByte(dataAddr() & 0xFF)
|
|
||||||
emitDataWord(toAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
def emitCodeFixup(toAddr)
|
|
||||||
{
|
|
||||||
// Src addr is reversed (i.e. it's hi then lo), and marked by hi-bit flag.
|
|
||||||
emitFixupByte((bytecodeAddr() >> 8) | 0x80)
|
|
||||||
emitFixupByte(bytecodeAddr() & 0xFF)
|
|
||||||
emitCodeWord(toAddr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packTextPrint(blk)
|
def packTextPrint(blk)
|
||||||
@ -2148,40 +2061,22 @@ class PackPartitions
|
|||||||
printWarning "empty text_print block, skipping."
|
printWarning "empty text_print block, skipping."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
def val = blk.value[0]
|
def text = getSingle(getSingle(getSingle(blk.value, 'VALUE').block, null, 'text').field, 'TEXT').text()
|
||||||
assert val.@name == 'VALUE'
|
outIndented("${blk.@type == 'text_print' ? 'scriptDisplayStr' : 'scriptDisplayStrNL'}(")
|
||||||
assert val.block.size() == 1
|
emitString(text)
|
||||||
def valBlk = val.block[0]
|
out << ")\n"
|
||||||
assert valBlk.@type == 'text'
|
|
||||||
assert valBlk.field.size() == 1
|
|
||||||
def fld = valBlk.field[0]
|
|
||||||
assert fld.@name == 'TEXT'
|
|
||||||
def text = fld.text()
|
|
||||||
//println " text: '$text'"
|
|
||||||
|
|
||||||
emitAuxString(text)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(blk.@type == 'text_print' ? vec_displayStr : vec_displayStrNL)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packClearWindow(blk)
|
def packClearWindow(blk)
|
||||||
{
|
{
|
||||||
assert blk.value.size() == 0
|
assert blk.value.size() == 0
|
||||||
//println " clearWindow"
|
outIndented("clearWindow()\n")
|
||||||
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_clrTextWindow)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packGetAnyKey(blk)
|
def packGetAnyKey(blk)
|
||||||
{
|
{
|
||||||
//println " get any key"
|
assert blk.value.size() == 0
|
||||||
|
outIndented("getUpperKey()\n")
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_getCharacter)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packIfStmt(blk)
|
def packIfStmt(blk)
|
||||||
@ -2190,152 +2085,74 @@ class PackPartitions
|
|||||||
printWarning "missing condition; skipping."
|
printWarning "missing condition; skipping."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert blk.value.size() == 1
|
def cond = getSingle(blk.value, 'IF0')
|
||||||
def cond = blk.value[0]
|
outIndented("if (")
|
||||||
assert cond.@name == 'IF0'
|
def ctype = getSingle(cond.block)
|
||||||
assert cond.block.size() == 1
|
switch (ctype.@type) {
|
||||||
assert cond.block[0].@type == 'text_getboolean'
|
case 'text_getboolean':
|
||||||
|
out << "getYN()"
|
||||||
//print " Conditional on getboolean,"
|
break
|
||||||
|
default:
|
||||||
emitCodeByte(0x54) // CALL
|
assert false : "Conditional on ${ctype.@type} not yet implemented."
|
||||||
emitCodeWord(vec_getYN)
|
}
|
||||||
emitCodeByte(0x4C) // BRFS
|
out << ")\n"
|
||||||
def branchStart = bytecodeAddr()
|
++indent
|
||||||
emitCodeWord(0) // placeholder until we know how far to jump over
|
packBlock(getSingle(getSingle(blk.statement).block))
|
||||||
|
--indent
|
||||||
assert blk.statement.size() == 1
|
outIndented("fin\n")
|
||||||
def doStmt = blk.statement[0]
|
|
||||||
assert doStmt.block.size() == 1
|
|
||||||
packBlock(doStmt.block[0])
|
|
||||||
|
|
||||||
// Now calculate and fix the relative branch
|
|
||||||
def branchEnd = bytecodeAddr()
|
|
||||||
def rel = branchEnd - branchStart
|
|
||||||
bytecode[branchStart] = (byte)(rel & 0xFF)
|
|
||||||
bytecode[branchStart+1] = (byte)((rel >> 8) & 0xFF)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packSetMap(blk)
|
def packSetMap(blk)
|
||||||
{
|
{
|
||||||
def mapNum, x=0, y=0, facing=0
|
assert blk.field.size() == 4
|
||||||
|
assert blk.field[0].@name == 'NAME'
|
||||||
blk.field.eachWithIndex { fld, idx ->
|
assert blk.field[1].@name == 'X'
|
||||||
switch (fld.@name)
|
assert blk.field[2].@name == 'Y'
|
||||||
{
|
assert blk.field[3].@name == 'FACING'
|
||||||
case 'NAME':
|
def mapName = blk.field[0].text()
|
||||||
def mapName = fld.text()
|
def mapNum = mapNames[mapName]
|
||||||
mapNum = mapNames[mapName]
|
|
||||||
if (!mapNum) {
|
if (!mapNum) {
|
||||||
printWarning "map '$mapName' not found; skipping set_map."
|
printWarning "map '$mapName' not found; skipping set_map."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
break
|
|
||||||
|
|
||||||
case 'X':
|
|
||||||
x = fld.text().toInteger()
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'Y':
|
|
||||||
y = fld.text().toInteger()
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'FACING':
|
|
||||||
facing = fld.text().toInteger()
|
|
||||||
assert facing >= 0 && facing <= 15
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert false : "Unknown field ${fld.@name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//println " Set map to '$mapName' (num $mapNum)"
|
|
||||||
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
assert mapNum[0] == '2D' || mapNum[0] == '3D'
|
assert mapNum[0] == '2D' || mapNum[0] == '3D'
|
||||||
emitCodeByte(mapNum[0] == '2D' ? 0 : 1)
|
def x = blk.field[1].text().toInteger()
|
||||||
emitCodeByte(0x2A) // CB
|
def y = blk.field[2].text().toInteger()
|
||||||
emitCodeByte(mapNum[1])
|
def facing = blk.field[3].text().toInteger()
|
||||||
emitCodeByte(0x2C) // CW
|
assert facing >= 0 && facing <= 15
|
||||||
emitCodeWord(x)
|
|
||||||
emitCodeByte(0x2C) // CW
|
outIndented("queue_setMap(${mapNum[0] == '2D' ? 0 : 1}, ${mapNum[1]}, $x, $y)\n")
|
||||||
emitCodeWord(y)
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
emitCodeByte(facing)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_setMap)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packSetPortrait(blk)
|
def packSetPortrait(blk)
|
||||||
{
|
{
|
||||||
def portraitNum, portraitName
|
def portraitName = getSingle(blk.field, 'NAME').text()
|
||||||
|
|
||||||
blk.field.eachWithIndex { fld, idx ->
|
|
||||||
switch (fld.@name)
|
|
||||||
{
|
|
||||||
case 'NAME':
|
|
||||||
portraitName = fld.text()
|
|
||||||
def portrait = portraits[portraitName]
|
def portrait = portraits[portraitName]
|
||||||
if (!portrait) {
|
if (!portrait) {
|
||||||
printWarning "portrait '$portraitName' not found; skipping set_portrait."
|
printWarning "portrait '$portraitName' not found; skipping set_portrait."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
portraitNum = portrait.num
|
outIndented("setPortrait(${portrait.num})\n")
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert false : "Unknown field ${fld.@name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
emitCodeByte(portraitNum)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_setPortrait)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packClrPortrait(blk)
|
def packClrPortrait(blk)
|
||||||
{
|
{
|
||||||
assert blk.field.size() == 0
|
assert blk.field.size() == 0
|
||||||
|
outIndented("clearPortrait()\n")
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_clrPortrait)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packSetSky(blk)
|
def packSetSky(blk)
|
||||||
{
|
{
|
||||||
assert blk.field.size() == 1
|
def color = getSingle(blk.field, 'COLOR').text().toInteger()
|
||||||
def fld = blk.field[0]
|
|
||||||
assert fld.@name == 'COLOR'
|
|
||||||
def color = fld.text().toInteger()
|
|
||||||
assert color >= 0 && color <= 17
|
assert color >= 0 && color <= 17
|
||||||
//println " Set sky to $color"
|
outIndented("setSky($color)\n")
|
||||||
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
emitCodeByte(color)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_setSky)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packSetGround(blk)
|
def packSetGround(blk)
|
||||||
{
|
{
|
||||||
assert blk.field.size() == 1
|
def color = getSingle(blk.field, 'COLOR').text().toInteger()
|
||||||
def fld = blk.field[0]
|
|
||||||
assert fld.@name == 'COLOR'
|
|
||||||
def color = fld.text().toInteger()
|
|
||||||
assert color >= 0 && color <= 17
|
assert color >= 0 && color <= 17
|
||||||
//println " Set ground to $color"
|
outIndented("setGround($color)\n")
|
||||||
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
emitCodeByte(color)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_setGround)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packTeleport(blk)
|
def packTeleport(blk)
|
||||||
@ -2348,52 +2165,25 @@ class PackPartitions
|
|||||||
def y = blk.field[1].text().toInteger()
|
def y = blk.field[1].text().toInteger()
|
||||||
def facing = blk.field[2].text().toInteger()
|
def facing = blk.field[2].text().toInteger()
|
||||||
assert facing >= 0 && facing <= 15
|
assert facing >= 0 && facing <= 15
|
||||||
//println " Teleport to ($x,$y) facing $facing"
|
outIndented("queue_teleport($x, $y, $facing)\n")
|
||||||
|
|
||||||
emitCodeByte(0x2C) // CW
|
|
||||||
emitCodeWord(x)
|
|
||||||
emitCodeByte(0x2C) // CW
|
|
||||||
emitCodeWord(y)
|
|
||||||
emitCodeByte(0x2A) // CB
|
|
||||||
emitCodeByte(facing)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_teleport)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def packMoveBackward(blk)
|
def packMoveBackward(blk)
|
||||||
{
|
{
|
||||||
assert blk.field.size() == 0
|
assert blk.field.size() == 0
|
||||||
//println " Move backward"
|
outIndented("moveBackward()\n")
|
||||||
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_moveBackward)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeInit(mapName, scripts, xRange, yRange, maxX, maxY)
|
def makeTriggerTbl(scripts, xRange, yRange)
|
||||||
{
|
{
|
||||||
//println " Script: special 'init'"
|
// Emit a predefinition for each function
|
||||||
startFunc(0)
|
scripts.eachWithIndex { script, idx ->
|
||||||
|
out << "predef script$idx\n"
|
||||||
|
}
|
||||||
|
|
||||||
// Emit the code the user has stored for the init script. While we're scanning,
|
// Collate all the matching location triggers into a sorted map.
|
||||||
// might as well also collate all the location triggers into a sorted map.
|
|
||||||
//
|
|
||||||
TreeMap triggers = [:]
|
TreeMap triggers = [:]
|
||||||
scripts.eachWithIndex { script, idx ->
|
scripts.eachWithIndex { script, idx ->
|
||||||
def name = getScriptName(script)
|
|
||||||
if (name != null && name.toLowerCase() == "init")
|
|
||||||
{
|
|
||||||
if (script.block.size() == 1) {
|
|
||||||
def proc = script.block[0]
|
|
||||||
assert proc.@type == "procedures_defreturn"
|
|
||||||
assert proc.statement.size() == 1
|
|
||||||
def stmt = proc.statement[0]
|
|
||||||
assert stmt.@name == "STACK"
|
|
||||||
stmt.block.each { packBlock(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
script.locationTrigger.each { trig ->
|
script.locationTrigger.each { trig ->
|
||||||
def x = trig.@x.toInteger()
|
def x = trig.@x.toInteger()
|
||||||
def y = trig.@y.toInteger()
|
def y = trig.@y.toInteger()
|
||||||
@ -2407,64 +2197,47 @@ class PackPartitions
|
|||||||
triggers[y] = [:] as TreeMap
|
triggers[y] = [:] as TreeMap
|
||||||
if (!triggers[y][x])
|
if (!triggers[y][x])
|
||||||
triggers[y][x] = []
|
triggers[y][x] = []
|
||||||
triggers[y][x].add((idx+1) * 5) // address of function
|
triggers[y][x].add("script$idx")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the map name
|
// Now output code for the table. First comes the X
|
||||||
def shortName = mapName.replaceAll(/[\s-]*[23][dD][-0-9]*$/, '').take(16)
|
|
||||||
|
|
||||||
// Code to register the map name, trigger table, and map extent.
|
|
||||||
emitAuxString(shortName)
|
|
||||||
emitCodeByte(0x26) // LA
|
|
||||||
emitCodeFixup(dataAddr())
|
|
||||||
emitCodeByte(0x2C) // CW
|
|
||||||
emitCodeWord(maxX)
|
|
||||||
emitCodeByte(0x2C) // CW
|
|
||||||
emitCodeWord(maxY)
|
|
||||||
emitCodeByte(0x54) // CALL
|
|
||||||
emitCodeWord(vec_setScriptInfo)
|
|
||||||
emitCodeByte(0x30) // DROP
|
|
||||||
|
|
||||||
// The table itself goes in the data segment. First comes the X
|
|
||||||
// and Y origins.
|
// and Y origins.
|
||||||
emitDataWord(xRange ? xRange[0] : 0)
|
out << "byte[] triggerTbl = ${xRange ? xRange[0] : 0}, ${yRange ? yRange[0] : 0} // origin X,Y\n"
|
||||||
emitDataWord(yRange ? yRange[0] : 0)
|
|
||||||
|
|
||||||
// Then the Y tables
|
// Then the Y tables
|
||||||
triggers.each { y, xs ->
|
triggers.each { y, xs ->
|
||||||
emitDataByte(y)
|
|
||||||
def size = 2 // 2 bytes for y+off
|
def size = 2 // 2 bytes for y+off
|
||||||
xs.each { x, funcAddrs ->
|
xs.each { x, funcs ->
|
||||||
size += funcAddrs.size() * 3 // plus 3 bytes per trigger (x, adrlo, adrhi)
|
size += funcs.size() * 3 // plus 3 bytes per trigger (x, adrlo, adrhi)
|
||||||
}
|
}
|
||||||
emitDataByte(size)
|
out << "byte = $y, $size // Y=$y, size=$size\n"
|
||||||
xs.each { x, funcAddrs ->
|
xs.each { x, funcs ->
|
||||||
funcAddrs.each { funcAddr ->
|
funcs.each { func ->
|
||||||
emitDataByte(x)
|
out << " byte = $x; word = @$func // X=$x\n"
|
||||||
emitDataFixup(funcAddr)
|
|
||||||
}
|
}
|
||||||
// Record a list of trigger locations for the caller's reference
|
// Record a list of trigger locations for the caller's reference
|
||||||
locationsWithTriggers << [x, y]
|
locationsWithTriggers << [x, y]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitDataByte(0xFF) // mark the end end of the trigger table
|
out << "byte = \$FF\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
def makeInit(mapName, scripts, maxX, maxY)
|
||||||
|
{
|
||||||
|
// Emit the code the user has stored for the init script.
|
||||||
|
scripts.each { script ->
|
||||||
|
if (script.block.size() == 1)
|
||||||
|
packBlock(getSingle(getSingle(script.block, null, 'procedures_defreturn').statement, 'STACK'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code to register the map name, trigger table, and map extent.
|
||||||
|
def shortName = mapName.replaceAll(/[\s-]*[23][dD][-0-9]*$/, '').take(16)
|
||||||
|
out << "setScriptInfo(\"$shortName\", triggerTbl, $maxX, $maxY)\n"
|
||||||
|
|
||||||
// All done with the init function.
|
// All done with the init function.
|
||||||
finishFunc()
|
out << "done\n"
|
||||||
}
|
|
||||||
|
|
||||||
def packFixups()
|
|
||||||
{
|
|
||||||
def buf = []
|
|
||||||
fixups.each { fromOff, fromType, toOff, toType ->
|
|
||||||
assert fromType == 'bytecode'
|
|
||||||
assert toType == 'data'
|
|
||||||
toOff += (nScripts+1) * 5
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user