Merge branch 'master' into newplasma

This commit is contained in:
Martin Haye 2017-09-03 07:00:38 -07:00
commit 5286d766df
12 changed files with 1231 additions and 686 deletions

View File

@ -19,11 +19,13 @@ import java.nio.ByteBuffer
import java.nio.channels.Channels import java.nio.channels.Channels
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
import java.util.Calendar
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
import java.util.LinkedHashMap import java.util.LinkedHashMap
import java.security.MessageDigest import java.security.MessageDigest
import javax.xml.bind.DatatypeConverter import javax.xml.bind.DatatypeConverter
import groovy.json.JsonOutput import groovy.json.JsonOutput
import groovy.util.Node
/** /**
* *
@ -81,6 +83,7 @@ class A2PackPartitions
def modules = [:] // module name to module.num, module.buf def modules = [:] // module name to module.num, module.buf
def bytecodes = [:] // module name to bytecode.num, bytecode.buf def bytecodes = [:] // module name to bytecode.num, bytecode.buf
def fixups = [:] // module name to fixup.num, fixup.buf def fixups = [:] // module name to fixup.num, fixup.buf
def gameFlags = [:] // flag name to number
def itemNameToFunc = [:] def itemNameToFunc = [:]
def playerNameToFunc = [:] def playerNameToFunc = [:]
@ -773,6 +776,12 @@ class A2PackPartitions
cache[key] = [hash:hash, data:buf] cache[key] = [hash:hash, data:buf]
} }
def updateEngineStamp(name, hash)
{
if (!cache.containsKey("engineStamp") || cache["engineStamp"].hash < hash)
cache["engineStamp"] = [hash:hash]
}
def grabEntireFromCache(kind, addTo, hash) def grabEntireFromCache(kind, addTo, hash)
{ {
if (cache.containsKey(kind) && cache[kind].hash == hash) { if (cache.containsKey(kind) && cache[kind].hash == hash) {
@ -1559,6 +1568,30 @@ class A2PackPartitions
} }
} }
/**
* Make a compact representation of a timestamp, useful as a version number
*/
def timestampToVersionNum(engineStamp, scenarioStamp)
{
Calendar cal = Calendar.getInstance()
cal.setTimeInMillis(engineStamp)
def year = cal.get(Calendar.YEAR)
def month = cal.get(Calendar.MONTH)
def day = cal.get(Calendar.DAY_OF_MONTH)
def hour = cal.get(Calendar.HOUR_OF_DAY)
def yearCode = year % 10
def monthCode = (month < 9) ? (char) (48+month+1) :
month == 9 ? 'o' :
month == 10 ? 'n' :
'd'
def hourCode = (char) (97 + hour) // 'a'=0, 'b'=1, etc.
def engineCode = String.format("%d%c%02d%c", yearCode, monthCode, day, hourCode)
def offset = (int) ((scenarioStamp - engineStamp) / (1000 * 60 * 60))
return String.format("%s%s%d", engineCode, offset < 0 ? "-" : ".", Math.abs(offset))
}
/** /**
* Make an index listing the partition number wherein each map and portrait can be found. * Make an index listing the partition number wherein each map and portrait can be found.
*/ */
@ -1566,6 +1599,12 @@ class A2PackPartitions
{ {
def tmp = ByteBuffer.allocate(5000) def tmp = ByteBuffer.allocate(5000)
// Start with the version number
def combinedVersion = timestampToVersionNum(cache["engineStamp"].hash, cache["scenarioStamp"].hash)
tmp.put((byte)(combinedVersion.length()))
combinedVersion.getBytes().each { b -> tmp.put((byte)b) }
// Then output 2D maps, 3d maps, and portraits
tmp.put((byte) maps2D.size()) tmp.put((byte) maps2D.size())
maps2D.each { k, v -> maps2D.each { k, v ->
tmp.put((byte) ((parseOrder(v.order) < 0) ? 255 : v.buf.partNum)) tmp.put((byte) ((parseOrder(v.order) < 0) ? 255 : v.buf.partNum))
@ -1584,6 +1623,8 @@ class A2PackPartitions
name:"resourceIndex", buf:code["resourceIndex"].buf] name:"resourceIndex", buf:code["resourceIndex"].buf]
part.chunks[["code", "resourceIndex"]] = chunk part.chunks[["code", "resourceIndex"]] = chunk
part.spaceUsed += calcChunkLen(chunk) part.spaceUsed += calcChunkLen(chunk)
return combinedVersion
} }
def fillAllDisks() def fillAllDisks()
@ -1637,7 +1678,7 @@ class A2PackPartitions
assert allMaps.isEmpty : "All data must fit within $MAX_DISKS disks." assert allMaps.isEmpty : "All data must fit within $MAX_DISKS disks."
// Add the special resource index to disk 1 // Add the special resource index to disk 1
addResourceIndex(partChunks[0]) def gameVersion = addResourceIndex(partChunks[0])
// And write out each disk // And write out each disk
partChunks.each { part -> partChunks.each { part ->
@ -1649,6 +1690,8 @@ class A2PackPartitions
def spaceUsed = part.spaceUsed // use var to avoid gigantic assert fail msg def spaceUsed = part.spaceUsed // use var to avoid gigantic assert fail msg
assert spaceUsed == partFile.length() assert spaceUsed == partFile.length()
} }
println "Game version: V $gameVersion"
} }
def writePartition(stream, partNum, chunks) def writePartition(stream, partNum, chunks)
@ -1859,6 +1902,7 @@ class A2PackPartitions
def uncompData = readBinary(inDir + "build/" + codeName + ".b") def uncompData = readBinary(inDir + "build/" + codeName + ".b")
addToCache("code", code, codeName, hash, compress(uncompData)) addToCache("code", code, codeName, hash, compress(uncompData))
updateEngineStamp(codeName, hash)
} }
def assembleCore(inDir) def assembleCore(inDir)
@ -1878,8 +1922,10 @@ class A2PackPartitions
def file = jitCopy( def file = jitCopy(
new File("build/tools/${name=="PRORWTS" ? "ProRWTS/PRORWTS2" : "PLASMA/src/PLVM02"}#4000")) new File("build/tools/${name=="PRORWTS" ? "ProRWTS/PRORWTS2" : "PLASMA/src/PLVM02"}#4000"))
hash = file.lastModified() hash = file.lastModified()
if (!grabFromCache("sysCode", sysCode, name, hash)) if (!grabFromCache("sysCode", sysCode, name, hash)) {
addToCache("sysCode", sysCode, name, hash, compress(readBinary(file.toString()))) addToCache("sysCode", sysCode, name, hash, compress(readBinary(file.toString())))
updateEngineStamp(name, hash)
}
} }
else { else {
hash = getLastDep(new File(inDir, "${name}.s")) hash = getLastDep(new File(inDir, "${name}.s"))
@ -1892,6 +1938,7 @@ class A2PackPartitions
addToCache("sysCode", sysCode, name, hash, addToCache("sysCode", sysCode, name, hash,
(name ==~ /loader|decomp/) ? [data:uncompData, len:uncompData.length, compressed:false] (name ==~ /loader|decomp/) ? [data:uncompData, len:uncompData.length, compressed:false]
: compress(uncompData)) : compress(uncompData))
updateEngineStamp(name, hash)
} }
} }
@ -1952,6 +1999,8 @@ class A2PackPartitions
addToCache("modules", modules, moduleName, hash, module) addToCache("modules", modules, moduleName, hash, module)
addToCache("bytecodes", bytecodes, moduleName, hash, bytecode) addToCache("bytecodes", bytecodes, moduleName, hash, bytecode)
addToCache("fixups", fixups, moduleName, hash, fixup) addToCache("fixups", fixups, moduleName, hash, fixup)
if (!(moduleName ==~ /.*(gs|gen)_.*/ || codeDir ==~ /.*mapScript.*/))
updateEngineStamp(moduleName, hash)
} }
def readAllCode() def readAllCode()
@ -1980,6 +2029,7 @@ class A2PackPartitions
compileModule("gen_enemies", "src/plasma/") compileModule("gen_enemies", "src/plasma/")
compileModule("gen_items", "src/plasma/") compileModule("gen_items", "src/plasma/")
compileModule("gen_players", "src/plasma/") compileModule("gen_players", "src/plasma/")
compileModule("gen_flags", "src/plasma/")
globalScripts.each { name, nArgs -> globalScripts.each { name, nArgs ->
compileModule("gs_"+name, "src/plasma/") compileModule("gs_"+name, "src/plasma/")
} }
@ -2015,6 +2065,40 @@ class A2PackPartitions
} }
} }
/*
* Scan all the scripts looking for flags, and make a mapping of flag name to number.
*/
def numberGameFlags(data)
{
String name = data.name().toString()
if (name == "{outlaw}gameData") {
gameFlags = [] as Set // temporary, until we have them all
data.global.scripts.script.each { numberGameFlags(it) }
data.map.scripts.script.each { numberGameFlags(it) }
// Now that we have them all, sort and assign numbers
def flagSet = gameFlags
gameFlags = [:]
flagSet.sort().each { flg -> gameFlags[flg] = gameFlags.size() }
}
else if (name == "{outlaw}block" &&
(data.@type == "interaction_get_flag" || data.@type == "interaction_set_flag"))
{
def els = data.field
assert els.size() == 1
def first = els[0]
assert first.@name == "NAME"
def flg = first.text().toLowerCase()
gameFlags << flg
}
else {
data.iterator().each {
if (it instanceof Node)
numberGameFlags(it)
}
}
}
def readCache() def readCache()
{ {
File cacheFile = new File("build/world.cache") File cacheFile = new File("build/world.cache")
@ -2127,11 +2211,14 @@ class A2PackPartitions
addResourceDep("map", curMapName, toType, toName) addResourceDep("map", curMapName, toType, toName)
} }
def pack(xmlPath, dataIn) def pack(xmlFile, dataIn)
{ {
// Save time by using cache of previous run // Save time by using cache of previous run
readCache() readCache()
// Record scenario timestamp
cache["scenarioStamp"] = [hash: xmlFile.lastModified()]
// Record global script names // Record global script names
recordGlobalScripts(dataIn) recordGlobalScripts(dataIn)
@ -2242,6 +2329,9 @@ class A2PackPartitions
// Number all the maps and record them with names // Number all the maps and record them with names
numberMaps(dataIn) numberMaps(dataIn)
// Assign a number to each game flag
numberGameFlags(dataIn)
// Form the translation from item name to function name (and ditto // Form the translation from item name to function name (and ditto
// for players) // for players)
allItemFuncs(dataIn.global.sheets.sheet) allItemFuncs(dataIn.global.sheets.sheet)
@ -2598,6 +2688,67 @@ end
return parseDice(val) return parseDice(val)
} }
def genAllFlags()
{
// Make constants
new File("build/src/plasma/gen_flags.plh.new").withWriter { out ->
out.println("// Generated code - DO NOT MODIFY BY HAND\n")
out.println("const flags_nameForNumber = 0")
out.println("const flags_numberForName = 2")
out.println()
gameFlags.each { name, num ->
out.println("const GF_${humanNameToSymbol(name, true)} = $num")
}
out.println("const NUM_GAME_FLAGS = ${gameFlags.size()}")
}
replaceIfDiff("build/src/plasma/gen_flags.plh")
// Generate code
new File("build/src/plasma/gen_flags.pla.new").withWriter { out ->
out.println("// Generated code - DO NOT MODIFY BY HAND")
out.println()
out.println("include \"gamelib.plh\"")
out.println("include \"globalDefs.plh\"")
out.println("include \"gen_flags.plh\"")
out.println()
out.println("predef _flags_nameForNumber(num)#1")
out.println("predef _flags_numberForName(name)#1")
out.println()
out.println("word[] funcTbl = @_flags_nameForNumber, @_flags_numberForName")
out.println()
gameFlags.each { name, num ->
out.println("byte[] SF_${humanNameToSymbol(name, true)} = ${escapeString(name.toUpperCase())}")
}
out.println()
gameFlags.each { name, num ->
if (num == 0)
out.print("word[] flagNames = ")
else
out.print("word = ")
out.println("@SF_${humanNameToSymbol(name, true)}")
}
out.println()
out.println("def _flags_nameForNumber(num)#1")
out.println(" if num >= 0 and num < NUM_GAME_FLAGS; return flagNames[num]; fin")
out.println(" return NULL")
out.println("end")
out.println()
out.println("def _flags_numberForName(name)#1")
out.println(" word num")
out.println(" for num = 0 to NUM_GAME_FLAGS-1")
out.println(" if streqi(flagNames[num], name); return num; fin")
out.println(" next")
out.println(" return -1")
out.println("end")
out.println()
// Lastly, the outer module-level code
out.println("return @funcTbl")
out.println("done")
}
replaceIfDiff("build/src/plasma/gen_flags.pla")
}
def genWeapon(func, row, out) def genWeapon(func, row, out)
{ {
out.println( out.println(
@ -2629,26 +2780,21 @@ end
"${parseModifier(row, "bonus-value", "bonus-attribute")})") "${parseModifier(row, "bonus-value", "bonus-attribute")})")
} }
def genAmmo(func, row, out)
{
out.println(
" return makeStuff(" +
"${escapeString(parseStringAttr(row, "name"))}, " +
"${escapeString(parseStringAttr(row, "ammo-kind"))}, " +
"${parseWordAttr(row, "price")}, " +
"${parseWordAttr(row, "max")}, " +
"${parseWordAttr(row, "store-amount")}, " +
"${parseDiceAttr(row, "loot-amount")})")
}
def genItem(func, row, out) def genItem(func, row, out)
{ {
out.println( def name = parseStringAttr(row, "name")
" return makeItem(" + def price = parseWordAttr(row, "price")
"${escapeString(parseStringAttr(row, "name"))}, " + def modifier = parseModifier(row, "bonus-value", "bonus-attribute")
"${parseWordAttr(row, "price")}, " + def kind = parseStringAttr(row, "ammo-kind")
"${parseModifier(row, "bonus-value", "bonus-attribute")}, " + def count = parseWordAttr(row, "count")
"${parseByteAttr(row, "number-of-uses")})") def storeAmount = parseWordAttr(row, "store-amount")
def lootAmount = parseDiceAttr(row, "loot-amount")
if ("$kind, $modifier, $count, $storeAmount, $lootAmount" != ", NULL, 0, 0, 0")
out.println(" return makeFancyItem(${escapeString(name)}, $price, " +
"${escapeString(kind)}, $modifier, $count, $storeAmount, $lootAmount)")
else
out.println(" return makePlainItem(${escapeString(name)}, $price)")
} }
def genPlayer(func, row, out) def genPlayer(func, row, out)
@ -2688,7 +2834,7 @@ end
def itemFunc = itemNameToFunc[name] def itemFunc = itemNameToFunc[name]
assert itemFunc : "Can't locate item '$name'" assert itemFunc : "Can't locate item '$name'"
if (num > 1) if (num > 1)
out.println(" addToList(@p=>p_items, setStuffCount(itemScripts()=>$itemFunc(), $num))") out.println(" addToList(@p=>p_items, setItemCount(itemScripts()=>$itemFunc(), $num))")
else else
out.println(" addToList(@p=>p_items, itemScripts()=>$itemFunc())") out.println(" addToList(@p=>p_items, itemScripts()=>$itemFunc())")
} }
@ -2849,24 +2995,22 @@ def makeWeapon_pt2(p, attack0, attack1, attack2, weaponRange, combatText, single
return p return p
end end
def makeStuff(name, kind, price, count, storeAmount, lootAmount) def makePlainItem(name, price)
word p; p = mmgr(HEAP_ALLOC, TYPE_STUFF) word p; p = mmgr(HEAP_ALLOC, TYPE_PLAIN_ITEM)
p=>s_name = mmgr(HEAP_INTERN, name) p=>s_name = mmgr(HEAP_INTERN, name)
p=>s_itemKind = mmgr(HEAP_INTERN, kind)
p=>w_price = price p=>w_price = price
p=>w_count = count
p=>w_storeAmount = storeAmount
p=>r_lootAmount = lootAmount
return p return p
end end
def makeItem(name, price, modifier, maxUses) def makeFancyItem(name, price, kind, modifiers, count, storeAmount, lootAmount)
word p; p = mmgr(HEAP_ALLOC, TYPE_ITEM) word p; p = mmgr(HEAP_ALLOC, TYPE_FANCY_ITEM)
p=>s_name = mmgr(HEAP_INTERN, name) p=>s_name = mmgr(HEAP_INTERN, name)
p=>w_price = price p=>w_price = price
p=>p_modifiers = modifier p=>s_itemKind = mmgr(HEAP_INTERN, kind)
p->b_maxUses = maxUses p=>p_modifiers = modifiers
p->b_curUses = 0 p=>w_count = count
p=>w_storeAmount = storeAmount
p=>r_lootAmount = lootAmount
return p return p
end end
@ -2880,7 +3024,7 @@ end
switch (typeName) { switch (typeName) {
case "weapon": genWeapon(func, row, out); break case "weapon": genWeapon(func, row, out); break
case "armor": genArmor(func, row, out); break case "armor": genArmor(func, row, out); break
case "ammo": genAmmo(func, row, out); break case "ammo": genItem(func, row, out); break
case "item": genItem(func, row, out); break case "item": genItem(func, row, out); break
default: assert false default: assert false
} }
@ -2993,11 +3137,11 @@ def makePlayer_pt2(p, health, level, aiming, handToHand, dodging, gender)#1
return p return p
end end
def setStuffCount(p, ct)#1 def setItemCount(p, ct)#1
if p->t_type == TYPE_STUFF if p->t_type == TYPE_FANCY_ITEM
p->w_count = ct p->w_count = ct
else else
fatal(\"stuffct\") fatal(\"itemct\")
fin fin
return p // for chaining return p // for chaining
end end
@ -3088,7 +3232,7 @@ end
return [name.trim(), animFrameNum, animFlags] return [name.trim(), animFrameNum, animFlags]
} }
def dataGen(xmlPath, dataIn) def dataGen(xmlFile, dataIn)
{ {
// When generating code, we need to use Unix linebreaks since that's what // When generating code, we need to use Unix linebreaks since that's what
// the PLASMA compiler expects to see. // the PLASMA compiler expects to see.
@ -3141,6 +3285,9 @@ end
// Before we can generate global script code, we need to identify and number all the maps. // Before we can generate global script code, we need to identify and number all the maps.
numberMaps(dataIn) numberMaps(dataIn)
// Assign a number to each game flag
numberGameFlags(dataIn)
// Form the translation from item name to function name (and ditto // Form the translation from item name to function name (and ditto
// for players) // for players)
allItemFuncs(dataIn.global.sheets.sheet) allItemFuncs(dataIn.global.sheets.sheet)
@ -3152,6 +3299,9 @@ end
genAllGlobalScripts(dataIn.global.scripts.script) genAllGlobalScripts(dataIn.global.scripts.script)
curMapName = null curMapName = null
// Generate a mapping of flags, for debugging purposes.
genAllFlags()
// Translate enemies, weapons, etc. to code // Translate enemies, weapons, etc. to code
genAllItems(dataIn.global.sheets.sheet) genAllItems(dataIn.global.sheets.sheet)
genAllEnemies(dataIn.global.sheets.sheet.find { it?.@name.equalsIgnoreCase("enemies") }) genAllEnemies(dataIn.global.sheets.sheet.find { it?.@name.equalsIgnoreCase("enemies") })
@ -3206,7 +3356,7 @@ end
def createHddImage() def createHddImage()
{ {
println "Creating hdd image." //println "Creating hdd image."
// Copy the combined core executable to the output directory // Copy the combined core executable to the output directory
copyIfNewer(new File("build/src/core/build/LEGENDOS.SYSTEM.sys#2000"), copyIfNewer(new File("build/src/core/build/LEGENDOS.SYSTEM.sys#2000"),
@ -3231,7 +3381,7 @@ end
def createFloppyImages() def createFloppyImages()
{ {
println "Creating floppy images." //println "Creating floppy images."
// We'll be copying stuff from the hdd directory // We'll be copying stuff from the hdd directory
def hddDir = new File("build/root") def hddDir = new File("build/root")
@ -3442,6 +3592,7 @@ end
out << "include \"../plasma/gamelib.plh\"\n" out << "include \"../plasma/gamelib.plh\"\n"
out << "include \"../plasma/globalDefs.plh\"\n" out << "include \"../plasma/globalDefs.plh\"\n"
out << "include \"../plasma/playtype.plh\"\n" out << "include \"../plasma/playtype.plh\"\n"
out << "include \"../plasma/gen_flags.plh\"\n"
out << "include \"../plasma/gen_images.plh\"\n" out << "include \"../plasma/gen_images.plh\"\n"
out << "include \"../plasma/gen_items.plh\"\n" out << "include \"../plasma/gen_items.plh\"\n"
out << "include \"../plasma/gen_modules.plh\"\n" out << "include \"../plasma/gen_modules.plh\"\n"
@ -3929,14 +4080,16 @@ end
def packGetFlag(blk) def packGetFlag(blk)
{ {
def name = getSingle(blk.field, 'NAME').text() def name = getSingle(blk.field, 'NAME').text().trim().toLowerCase()
out << "getGameFlag(${escapeString(name)})" assert gameFlags.containsKey(name)
out << "getGameFlag(GF_${humanNameToSymbol(name, true)})"
} }
def packChangeFlag(blk) def packChangeFlag(blk)
{ {
def name = getSingle(blk.field, 'NAME').text() def name = getSingle(blk.field, 'NAME').text().trim().toLowerCase()
outIndented("setGameFlag(${escapeString(name)}, ${blk.@type == 'interaction_set_flag' ? 1 : 0})\n") assert gameFlags.containsKey(name)
outIndented("setGameFlag(GF_${humanNameToSymbol(name, true)}, ${blk.@type == 'interaction_set_flag' ? 1 : 0})\n")
} }
def isStringExpr(blk) def isStringExpr(blk)
@ -4009,6 +4162,10 @@ end
} }
} }
def packLogicNegate(blk) {
out << "!("; packExpr(getSingle(blk.value, "BOOL").block[0]); out << ")"
}
def packMathArithmetic(blk) def packMathArithmetic(blk)
{ {
def op = getSingle(blk.field, "OP").text() def op = getSingle(blk.field, "OP").text()
@ -4078,6 +4235,9 @@ end
case 'logic_operation': case 'logic_operation':
packLogicOperation(blk) packLogicOperation(blk)
break break
case 'logic_negate':
packLogicNegate(blk)
break
case 'variables_get': case 'variables_get':
packVarGet(blk) packVarGet(blk)
break break

View File

@ -747,15 +747,8 @@ gcHash_chk: !zone
rts rts
.corrup jmp heapCorrupt .corrup jmp heapCorrupt
!if DEBUG = 0 {
debugOnly:
jsr inlineFatal : !text "DebugOnly",0
}
; Verify integrity of memory manager structures ; Verify integrity of memory manager structures
memCheck: !zone memCheck: !zone
!if DEBUG = 0 { jmp debugOnly }
!if DEBUG {
jsr heapCheck ; heap check (if there is one) jsr heapCheck ; heap check (if there is one)
ldx #0 ; check main bank ldx #0 ; check main bank
jsr .chk jsr .chk
@ -811,7 +804,6 @@ heapCheck: !zone
cmp heapEndPg ; or >= than end of heap cmp heapEndPg ; or >= than end of heap
bcc .tscan bcc .tscan
; fall through to heapCorrupt... ; fall through to heapCorrupt...
} ; if DEBUG
heapCorrupt: heapCorrupt:
+prWord pTmp +prWord pTmp
@ -1333,16 +1325,10 @@ aux_dispatch:
!if SANITY_CHECK { !if SANITY_CHECK {
saneStart: !zone { saneStart: !zone {
sta saneEnd+2 ; save cmd num for end-checking sta saneEnd+2 ; save cmd num for end-checking
pha cmp #ADVANCE_ANIMS
tya beq .skip
pha
txa
pha pha
jsr saneCheck jsr saneCheck
pla
tax
pla
tay
+prChr 'M' +prChr 'M'
lda isAuxCmd lda isAuxCmd
beq + beq +
@ -1359,7 +1345,7 @@ saneStart: !zone {
beq + beq +
+prY +prY
+ pla + pla
rts .skip rts
} }
saneCheck: !zone { saneCheck: !zone {
@ -1373,6 +1359,8 @@ saneCheck: !zone {
saneEnd: !zone { saneEnd: !zone {
pha pha
lda #$11 ; self-modified earlier by saneStart lda #$11 ; self-modified earlier by saneStart
cmp #ADVANCE_ANIMS
beq .skip
cmp #REQUEST_MEMORY cmp #REQUEST_MEMORY
beq .val beq .val
cmp #QUEUE_LOAD cmp #QUEUE_LOAD
@ -1385,17 +1373,9 @@ saneEnd: !zone {
bne .noval bne .noval
.val +prStr : !text "->",0 .val +prStr : !text "->",0
+prYX +prYX
.noval tya .noval jsr saneCheck
pha
txa
pha
jsr saneCheck
+prStr : !text "m.",0 +prStr : !text "m.",0
pla .skip pla
tax
pla
tay
pla
rts rts
} }
} }
@ -2787,6 +2767,7 @@ advSingleAnim:
rts rts
.dbgout +crout .dbgout +crout
+waitKey +waitKey
bit $c050
sta setAuxRd sta setAuxRd
sta setAuxWr sta setAuxWr
rts rts
@ -2828,12 +2809,14 @@ applyPatch:
; loop to skip patches until we find the right one ; loop to skip patches until we find the right one
- dec reqLen ; it starts at 1, which means first patch. - dec reqLen ; it starts at 1, which means first patch.
beq + beq +
ldy #1 ldy #0
lda (pSrc),y ; low byte of patch len
pha
iny
lda (pSrc),y ; hi byte of patch len lda (pSrc),y ; hi byte of patch len
inx ; -> pSrc+1 inx ; -> pSrc+1
jsr .ptradd ; skip by # pages in patch jsr .ptradd ; skip by # pages in patch
dey pla ; get lo byte of len back
lda (pSrc),y ; low byte of patch len
jsr .srcadd ; skip pSrc past last partial page in patch jsr .srcadd ; skip pSrc past last partial page in patch
jmp - jmp -
+ !if DEBUG = 2 { jsr .dbgC2 } + !if DEBUG = 2 { jsr .dbgC2 }

View File

@ -132,7 +132,7 @@ def rollPlayerHit(pPlayer, pWeapon, pEnemy, sAction)
roll = rollPercentileWithLuck(-(pPlayer->b_luck)) // luck can reduce roll = increase chance to hit roll = rollPercentileWithLuck(-(pPlayer->b_luck)) // luck can reduce roll = increase chance to hit
if combatDebug; displayf2("Roll=%d, need <%d\n", roll, abs(roll < chance)); getUpperKey(); fin if combatDebug; displayf2("Roll=%d, need <%d\n", roll, abs(roll < chance)); getUpperKey(); fin
if roll >= chance if roll >= chance
setPlural(0) isPlural = FALSE
displayf3("\n%s %s at %s but misses.\n", pPlayer=>s_name, sAction, pEnemy=>s_name) displayf3("\n%s %s at %s but misses.\n", pPlayer=>s_name, sAction, pEnemy=>s_name)
return FALSE return FALSE
fin fin
@ -143,7 +143,7 @@ end
def rollEnemyDodge(pPlayer, pEnemy, sAction) def rollEnemyDodge(pPlayer, pEnemy, sAction)
// Enemy chance to dodge is taken from their chance to hit divided by 2 // Enemy chance to dodge is taken from their chance to hit divided by 2
if (rand16() % 100) < (pEnemy->b_chanceToHit / 2) if (rand16() % 100) < (pEnemy->b_chanceToHit / 2)
setPlural(0) isPlural = FALSE
displayf3("\n%s %s at %s, ", pPlayer=>s_name, sAction, pEnemy=>s_name) displayf3("\n%s %s at %s, ", pPlayer=>s_name, sAction, pEnemy=>s_name)
displayf1("but %s dodges.\n", pEnemy=>s_name) displayf1("but %s dodges.\n", pEnemy=>s_name)
return TRUE return TRUE
@ -157,7 +157,7 @@ def damageEnemy(pPlayer, pEnemy, dmg, sAction)#0
displayf3("\nenemy health: %d-%d=%d\n", pEnemy=>w_health, dmg, pEnemy=>w_health-dmg) displayf3("\nenemy health: %d-%d=%d\n", pEnemy=>w_health, dmg, pEnemy=>w_health-dmg)
getUpperKey getUpperKey
fin fin
setPlural(0) isPlural = FALSE
buildString(@addToString) buildString(@addToString)
printf3("\n%s %s %s ", pPlayer=>s_name, sAction, pEnemy=>s_name) printf3("\n%s %s %s ", pPlayer=>s_name, sAction, pEnemy=>s_name)
printf1("for %d damage.", dmg) printf1("for %d damage.", dmg)
@ -290,7 +290,7 @@ def reload(pl, pWeapon, echo)#0
word item word item
byte orig, n byte orig, n
setPlural(FALSE) isPlural = FALSE
// If ammo type is null, it means weapon doesn't use ammo in traditional sense. // If ammo type is null, it means weapon doesn't use ammo in traditional sense.
if !pWeapon=>s_ammoKind if !pWeapon=>s_ammoKind
@ -301,7 +301,7 @@ def reload(pl, pWeapon, echo)#0
// Find matching ammo // Find matching ammo
item = pl=>p_items item = pl=>p_items
while item while item
if item->t_type == TYPE_STUFF if item->t_type == TYPE_FANCY_ITEM
if streqi(item=>s_itemKind, pWeapon=>s_ammoKind); break; fin if streqi(item=>s_itemKind, pWeapon=>s_ammoKind); break; fin
fin fin
item = item=>p_nextObj item = item=>p_nextObj
@ -366,7 +366,7 @@ def displayOpponents()#0
fin fin
first = FALSE first = FALSE
count = countListFiltered(p=>p_enemies, p_nextObj, @canFight) count = countListFiltered(p=>p_enemies, p_nextObj, @canFight)
setPlural(count <> 1) isPlural = count <> 1
if (p=>p_enemies=>r_groupSize == 0) if (p=>p_enemies=>r_groupSize == 0)
displayf2("%s at %d'", p=>p_enemies=>s_name, p->b_enemyGroupRange) displayf2("%s at %d'", p=>p_enemies=>s_name, p->b_enemyGroupRange)
else else
@ -678,7 +678,7 @@ def enemyCombatTurn(pe)#1
pl = randomFromListFiltered(global=>p_players, p_nextObj, @canFight) pl = randomFromListFiltered(global=>p_players, p_nextObj, @canFight)
if !pl; return FALSE; fin if !pl; return FALSE; fin
setPlural(FALSE) isPlural = FALSE
displayf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name) displayf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name)
// Roll to hit // Roll to hit
@ -851,7 +851,7 @@ def addItem(pl, pItem)#1
word pComp word pComp
pComp = scanForNamedObj(pl=>p_items, pItem=>s_name) pComp = scanForNamedObj(pl=>p_items, pItem=>s_name)
if pComp if pComp
if pItem->t_type == TYPE_STUFF if pItem->t_type == TYPE_FANCY_ITEM
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count) pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
return TRUE return TRUE
else else
@ -882,16 +882,17 @@ def collectLootAndXP()#2
mmgr(FINISH_LOAD, 0) mmgr(FINISH_LOAD, 0)
itemFunc = randomFromArray(pItemsModule()=>items_forLootCode(enemy=>s_lootCode)) itemFunc = randomFromArray(pItemsModule()=>items_forLootCode(enemy=>s_lootCode))
pItem = itemFunc() pItem = itemFunc()
if pItem->t_type == TYPE_STUFF if pItem->t_type == TYPE_FANCY_ITEM
pItem=>w_count = rollDiceWithLuck(pItem=>r_lootAmount, global=>p_players->b_luck) pItem=>w_count = rollDiceWithLuck(pItem=>r_lootAmount, global=>p_players->b_luck)
fin fin
if addItem(global=>p_players, pItem) if addItem(global=>p_players, pItem)
if pItem->t_type == TYPE_STUFF and pItem=>w_count > 1 displayStr("You find ")
setPlural(TRUE) if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
displayf2("You find %d %s! ", pItem=>w_count, pItem=>s_name) isPlural = TRUE
displayf2("%d %s! ", pItem=>w_count, pItem=>s_name)
else else
setPlural(FALSE) isPlural = FALSE
displayf2("You find %s%s! ", anOrA(pItem=>s_name), pItem=>s_name) displayf2("%s%s! ", anOrA(pItem=>s_name), pItem=>s_name)
fin fin
fin fin
lootedItem = TRUE lootedItem = TRUE
@ -908,6 +909,11 @@ end
def startCombat(mapCode)#1 def startCombat(mapCode)#1
word p, p2, n, s word p, p2, n, s
// Pre-load some global funcs
mmgr(QUEUE_LOAD, GS_COMBAT_INTRO<<8 | RES_TYPE_MODULE)
mmgr(QUEUE_LOAD, GS_COMBAT_PROMPT<<8 | RES_TYPE_MODULE)
mmgr(QUEUE_LOAD, GS_ENEMY_INTRO<<8 | RES_TYPE_MODULE)
// Setup // Setup
isFleeing = FALSE isFleeing = FALSE
combatDebug = FALSE combatDebug = FALSE
@ -915,6 +921,7 @@ def startCombat(mapCode)#1
// Display portrait of first group // Display portrait of first group
setPortrait(global=>p_enemyGroups=>p_enemies->b_image) setPortrait(global=>p_enemyGroups=>p_enemies->b_image)
mmgr(FINISH_LOAD, 0)
// Clear keyboard stobe, because while wandering the map, the player may have // Clear keyboard stobe, because while wandering the map, the player may have
// queued up movement keys, which are made obsolete by the surprise of combat. // queued up movement keys, which are made obsolete by the surprise of combat.
@ -932,7 +939,7 @@ def startCombat(mapCode)#1
p = global=>p_enemyGroups p = global=>p_enemyGroups
while p while p
n = countList(p=>p_enemies) n = countList(p=>p_enemies)
setPlural(n <> 1) isPlural = n <> 1
s = callGlobalFunc(GS_ENEMY_INTRO, 0, 0, 0) s = callGlobalFunc(GS_ENEMY_INTRO, 0, 0, 0)
displayf2(s, n, p=>p_enemies=>s_name) displayf2(s, n, p=>p_enemies=>s_name)
p = p=>p_nextObj p = p=>p_nextObj

View File

@ -37,6 +37,7 @@ predef _newOrLoadGame(ask)#1
word[] funcTbl = @_saveGame, @_loadGame, @_newOrLoadGame word[] funcTbl = @_saveGame, @_loadGame, @_newOrLoadGame
byte[] game1_filename = "GAME.1.SAVE" byte[] game1_filename = "GAME.1.SAVE"
byte[] legendos_filename = "LEGENDOS.SYSTEM"
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Definitions used by assembly code // Definitions used by assembly code
@ -223,15 +224,23 @@ def _rwGame(cmd)#0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def _saveGame()#1 def saveInternal()#0
// Never save corrupted heap
mmgr(CHECK_MEM, 0)
// Perform garbage collection and record the size of the heap so we can restore it correctly // Perform garbage collection and record the size of the heap so we can restore it correctly
global=>w_heapSize = mmgr(HEAP_COLLECT, 0) - HEAP_BOTTOM global=>w_heapSize = mmgr(HEAP_COLLECT, 0) - HEAP_BOTTOM
// Copy data to main memory, and write it out. // Copy data to main memory, and write it out.
showMapName("Saving game...")
memcpy(HEAP_BOTTOM, LOAD_SAVE_BUF, HEAP_SIZE) // LC to low mem memcpy(HEAP_BOTTOM, LOAD_SAVE_BUF, HEAP_SIZE) // LC to low mem
_rwGame(RWTS_WRITE) _rwGame(RWTS_WRITE)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def _saveGame()#1
showMapName("Saving game...")
saveInternal()
return 0 return 0
end end
@ -246,12 +255,12 @@ def loadInternal()#1
p_loaded = LOAD_SAVE_BUF p_loaded = LOAD_SAVE_BUF
if p_loaded=>w_heapSize == 0 if p_loaded=>w_heapSize == 0
return FALSE // no game saved yet return FALSE // no game saved yet
fin elsif p_loaded=>w_heapSize < 100 or p_loaded=>w_heapSize > HEAP_SIZE or p_loaded=>w_typeHash <> typeHash
if p_loaded=>w_heapSize < 100 or p_loaded=>w_heapSize > HEAP_SIZE fatal("Incompatible game file.")
fatal("Corrupt game file.")
fin fin
memcpy(LOAD_SAVE_BUF, HEAP_BOTTOM, HEAP_SIZE) // low mem to LC memcpy(LOAD_SAVE_BUF, HEAP_BOTTOM, HEAP_SIZE) // low mem to LC
initHeap(p_loaded=>w_heapSize) initHeap(p_loaded=>w_heapSize)
mmgr(CHECK_MEM, 0) // make sure heap is valid
return TRUE return TRUE
end end
@ -270,21 +279,123 @@ def _loadGame()#1
return 0 return 0
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def gameExists()#1
word p_loaded
// Load first part of save game into mem... 1 block should be plenty to verify it's real.
if callProRWTS(RWTS_READ | RWTS_OPENDIR, @game1_filename, LOAD_SAVE_BUF, 512) == 0
// If heap size is reasonable and type hash matches, chances are high that it's a real save game.
p_loaded = LOAD_SAVE_BUF
if p_loaded=>w_heapSize >= 100 and p_loaded=>w_heapSize <= HEAP_SIZE and p_loaded=>w_typeHash == typeHash
return TRUE
fin
fin
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def getTextKey()#1
byte key
^$c053
key = getUpperKey()
^$c052
textHome()
^$25 = 20
return key
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def pressAnyKey()#0
puts("\n and press any key to continue.")
getTextKey()
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def reinsert()#0
while TRUE
puts(" Re-insert disk 1")
pressAnyKey()
if callProRWTS(RWTS_READ | RWTS_OPENDIR, @legendos_filename, LOAD_SAVE_BUF, 512) == 0
break
fin
puts("\n ")
beep()
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def importGame()#1
puts("\n Insert disk for import")
pressAnyKey()
if gameExists()
loadInternal()
puts("\n Game imported.")
reinsert()
saveInternal()
return TRUE
fin
puts("\n Not found.")
reinsert()
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def isNameChar(ch)
when ch
is '.'
is ','
is '-'
is '\''
is ' '
return TRUE
wend
if ch >= 'A' and ch <= 'Z'; return TRUE; fin
if ch >= '0' and ch <= '9'; return TRUE; fin
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def isValidName(name)
byte len, c
len = ^name
if !len; return FALSE; fin
if ^(name+1) == ' '; return FALSE; fin // don't allow space at start
while len > 0
len--
name++
c = charToUpper(^name)
if c == ' ' and len == 0; return FALSE; fin // don't allow space at end
if !isNameChar(c); return FALSE; fin
loop
return TRUE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def getCharacterName()#0 def getCharacterName()#0
word cursX, cursY word cursX, cursY
displayStr("\nCharacter name?\n") while TRUE
displayStr("Character name?\n")
cursX, cursY = getCursor() cursX, cursY = getCursor()
setWindow(cursY+24, cursY+24+18, cursX+154, cursX+154+62) setWindow(cursY+24, cursY+24+18, cursX+154, cursX+154+62)
clearWindow() clearWindow()
global=>p_players=>s_name = getStringResponse() global=>p_players=>s_name = getStringResponse()
setWindow2() setWindow2()
setCursor(cursX, cursY) setCursor(cursX, cursY)
if isValidName(global=>p_players=>s_name); break; fin
displayStr("\nInvalid name.\n\n")
beep()
loop
end end
def getCharacterGender()#0 def getCharacterGender()#0
byte gender
repeat
displayStr("\n\nGender? (M/F/N/...) \n") displayStr("\n\nGender? (M/F/N/...) \n")
global=>p_players->c_gender = getUpperKey() gender = getUpperKey()
displayChar(global=>p_players->c_gender) displayChar(gender)
until gender >= 'A' and gender <= 'Z'
global=>p_players->c_gender = gender
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -303,6 +414,7 @@ def newGame()#0
showMapName("New game") showMapName("New game")
setWindow2() setWindow2()
newGameModule()() newGameModule()()
clearWindow()
getCharacterName() getCharacterName()
getCharacterGender() getCharacterGender()
if global=>p_players->b_skillPoints if global=>p_players->b_skillPoints
@ -310,52 +422,30 @@ def newGame()#0
fin fin
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def gameExists()#1
word p_loaded
// Load first part of save game into mem... 1 block should be plenty to verify it's real.
if callProRWTS(RWTS_READ | RWTS_OPENDIR, @game1_filename, LOAD_SAVE_BUF, 512) == 0
// If heap size is reasonable, assume it's a real save game. (Hash will be checked if the
// user chooses to actually load the game)
p_loaded = LOAD_SAVE_BUF
if p_loaded=>w_heapSize >= 100 and p_loaded=>w_heapSize <= HEAP_SIZE
return TRUE
fin
fin
return FALSE
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def _newOrLoadGame(ask)#1 def _newOrLoadGame(ask)#1
byte key byte key
if !gameExists() if !gameExists()
newGame(); return 1 newGame(); return 1
elsif !ask
loadInternal(); return 0
fin fin
if !ask
loadInternal()
return 0
fin
textHome()
^$c053
^$25 = 20
puts("\n N)ew game, or L)oad last game? ")
while TRUE while TRUE
key = getUpperKey() textHome()
if key == 'N' ^$25 = 20
^$c052 puts("\n Game: N)ew, L)oad, I)mport?")
newGame() key = getTextKey()
return 1 when key
elsif key == 'L' is 'N'
^$c052 newGame(); return 1
if loadInternal() is 'L'
return 0 if loadInternal(); return 0; fin
fin break
fin is 'I'
if importGame(); return 0; fin
wend
beep() beep()
loop loop
return 0 return 0

View File

@ -39,6 +39,7 @@ import gamelib
predef countArray(arr)#1 predef countArray(arr)#1
predef countList(p)#1 predef countList(p)#1
predef countListFiltered(p, offset, filterFunc)#1 predef countListFiltered(p, offset, filterFunc)#1
predef crout()#0
predef displayChar(chr)#0 predef displayChar(chr)#0
predef displayf1(fmt, arg1)#0 predef displayf1(fmt, arg1)#0
predef displayf2(fmt, arg1, arg2)#0 predef displayf2(fmt, arg1, arg2)#0
@ -46,7 +47,6 @@ import gamelib
predef displayStr(str)#0 predef displayStr(str)#0
predef encodeDice(nDice, dieSize, add)#1 predef encodeDice(nDice, dieSize, add)#1
predef fatal(msg)#1 predef fatal(msg)#1
predef finalWin()#0
predef finishString(isPlural)#1 predef finishString(isPlural)#1
predef flipToPage1()#0 predef flipToPage1()#0
predef getCharResponse()#1 predef getCharResponse()#1
@ -65,14 +65,15 @@ import gamelib
predef initPlayerXP(player)#0 predef initPlayerXP(player)#0
predef loadFrameImg(img)#0 predef loadFrameImg(img)#0
predef loadMainFrameImg()#0 predef loadMainFrameImg()#0
predef lookupResourcePart(sectionNum, resourceNum)#1
predef makeModifier(name, value)#1 predef makeModifier(name, value)#1
predef max(a, b)#1 predef max(a, b)#1
predef memcpy(pSrc, pDst, len)#0 predef memcpy(pSrc, pDst, len)#0
predef min(a, b)#1 predef min(a, b)#1
predef mmgr(cmd, wordParam)#1 predef mmgr(cmd, wordParam)#1
predef moveWayBackward()#1 predef moveWayBackward()#1
predef numToPlayer(num)#1
predef parseDec(str)#1 predef parseDec(str)#1
predef parseDecWithDefault(str, default)#1
predef partyHasPlayer(playerName)#1 predef partyHasPlayer(playerName)#1
predef pause(count)#0 predef pause(count)#0
predef payGold(amount)#1 predef payGold(amount)#1
@ -93,7 +94,6 @@ import gamelib
predef rawDisplayf3(fmt, arg1, arg2, arg3)#0 predef rawDisplayf3(fmt, arg1, arg2, arg3)#0
predef rawDisplayStr(str)#0 predef rawDisplayStr(str)#0
predef rdkey()#1 predef rdkey()#1
predef readStr()#1
predef removeFromList(pList, toRemove)#0 predef removeFromList(pList, toRemove)#0
predef removePlayerFromParty(playerName)#0 predef removePlayerFromParty(playerName)#0
predef rightJustifyNum(num, rightX)#0 predef rightJustifyNum(num, rightX)#0
@ -116,7 +116,6 @@ import gamelib
predef setMap(is3D, num, x, y, dir)#0 predef setMap(is3D, num, x, y, dir)#0
predef setMapWindow()#0 predef setMapWindow()#0
predef setBigWindow()#0 predef setBigWindow()#0
predef setPlural(flg)#0
predef setPortrait(portraitNum)#0 predef setPortrait(portraitNum)#0
predef setScriptInfo(mapName, trigTbl, wdt, hgt)#0 predef setScriptInfo(mapName, trigTbl, wdt, hgt)#0
predef setSky(num)#0 predef setSky(num)#0
@ -147,6 +146,8 @@ import gamelib
word groundNum word groundNum
byte portraitNum byte portraitNum
word pGodModule word pGodModule
word typeHash
byte isPlural
/////////// Shared string constants ////////////// /////////// Shared string constants //////////////

View File

@ -60,7 +60,6 @@ predef doRender()#0
predef playerDeath()#0 predef playerDeath()#0
predef startGame(ask)#0 predef startGame(ask)#0
predef showAnimFrame()#0 predef showAnimFrame()#0
predef finalWin()#0
predef showParty()#0 predef showParty()#0
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -77,12 +76,12 @@ byte renderLoaded = FALSE
byte texturesLoaded = FALSE byte texturesLoaded = FALSE
byte textDrawn = FALSE byte textDrawn = FALSE
byte textClearCountdown = 0 byte textClearCountdown = 0
byte isPlural = FALSE export byte isPlural = 0 // valid values: 0 or $40
byte skipEncounterCheck = FALSE byte inScript = FALSE
export word skyNum = 9 export word skyNum = 9
export word groundNum = 10 export word groundNum = 10
export byte portraitNum = 1 export byte portraitNum = 0
word triggerOriginX, triggerOriginY word triggerOriginX, triggerOriginY
word triggerTbl word triggerTbl
@ -97,6 +96,7 @@ word pResourceIndex = NULL
word pGlobalTileset = NULL word pGlobalTileset = NULL
byte curMapPartition = 0 byte curMapPartition = 0
export word pGodModule = NULL export word pGodModule = NULL
export word typeHash = 0
// Queue setMap / teleport / start_encounter, since otherwise script might be replaced while executing // Queue setMap / teleport / start_encounter, since otherwise script might be replaced while executing
byte q_mapIs3D = 0 byte q_mapIs3D = 0
@ -405,78 +405,73 @@ export asm finishString(isPlural)#1
sta cswl sta cswl
lda #$FD lda #$FD
sta cswh sta cswh
bit fixedRTS; V flag for prev-is-punctuation clv ; V flag for prev-is-alpha
ldy #1 ; dest offset in Y ldy #0 ; dest offset in Y (will be incremented before store)
ldx #1 ; source offset in X ldx #0 ; source offset in X (will be incremented before load)
cpx inbuf cpx inbuf
beq + ; only process if string has at least 1 char beq .done ; failsafe: handle zero-length string
bcs .done
+ sty tmp+1 ; offset of last punctuation
.fetch .fetch
lda inbuf,x inx
cmp #"(" lda inbuf,x ; get next input char
iny
sta inbuf,y ; by default copy the char to output
cmp #"(" ; plural processing triggered by parentheses
bne .notpar bne .notpar
bvs .notpar ; skip paren processing right punctuation bvc .notpar ; but only parens directly after alpha char, e.g. preserving "Happy (and safe)."
lda tmp ; check isPlural flag
bne .plurpr dey ; undo copy of the paren
- lda inbuf,x ; it's singular, so skip everything in parens stx tmp+1 ; save position in input
cmp #")" dex ; needed for failsafe operation
beq .next lda tmp ; set copy flag (V) initially
bne .findsl ; to same as isPlural flag
clv
.findsl ; see if there's a slash within the parens
inx inx
cpx inbuf cpx inbuf
bne - lda inbuf,x
beq .done ; handle missing trailing paren
.plurpr
inx ; it's plural, so copy everything within the parens
lda inbuf,x ; copy characters
cpx inbuf ; handle missing trailing paren
beq +
bcs .store
+ cmp #")" ; go until we reach ending paren
beq .next
sta inbuf,y
iny
bne .plurpr ; always taken
.notpar
cmp #"/" cmp #"/"
bne .notsl bne +
bvs .notsl ; skip slash processing right after punctuation php
lda tmp ; check isPlural flag pla
bne .plursl eor #$40 ; flip V flag, meaning singular text is before slash, plural after.
- inx ; loop that skips plural form pha
cpx inbuf plp
+ cmp #")" ; scan until ending paren
beq + beq +
bcs .done ; handle end of string cpx inbuf
+ lda inbuf,x bcc .findsl ; loop to scan next char
cmp #"A" ; eat letters (and stop when we hit punctuation) bcs .done ; failsafe: handle missing end-paren (always taken)
bcs - + ldx tmp+1 ; get back to start of parens
bcc .store ; copy the ending punctuation and continue normal processing ; copy mode flag is now in V: if slash present, single=copy, plural=nocopy
.plursl ; if no slash: single=nocopy, plural=copy
ldy tmp+1 ; erase singular form by backing up to prev punc .plup
iny ; plus 1 to retain prev punc inx
bne .next ; resume regular copying of the plural form lda inbuf,x
cmp #"/"
.notsl bne +
cmp #"A" ; if <= ASCII "A", consider it punctuation php
bcc + pla
clv ; clear last-is-punc flag eor #$40 ; flip from copying to not-copying, or vice-versa
bvc .store ; always taken pha
+ bit fixedRTS; set prev-is-punc flag plp
sty tmp+1 ; save dest offset of last punctuation bcs .plup ; always taken
+ cmp #")"
.store beq .notpar ; stop at closing paren
sta inbuf,y ; save to dest bvc .plup ; if not in copy mode, skip copy
iny iny
sta inbuf,y ; else do copy
bne .plup ; always taken
.notpar
bit fixedRTS; set prev-is-alpha flag
cmp #"A" ; if >= ASCII "A", consider it alpha
bcs .next
clv ; clear prev-is-alpha flag
.next .next
inx
cpx inbuf ; compare src offset to length cpx inbuf ; compare src offset to length
bcc .fetch ; loop while less than bcc .fetch ; loop while less than
beq .fetch ; or equal
.done .done
dey
sty inbuf ; save new length sty inbuf ; save new length
lda #<inbuf ; return pointer to string lda #<inbuf ; return pointer to string
ldy #>inbuf ldy #>inbuf
@ -729,7 +724,7 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Print a carriage return // Print a carriage return
asm crout()#0 export asm crout()#0
+asmPlasmNoRet 0 +asmPlasmNoRet 0
lda #$8D lda #$8D
jmp _safeCout jmp _safeCout
@ -743,28 +738,6 @@ export asm beep()#0
rts rts
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Read a string from the keyboard, turn it into a PLASMA string and return a pointer to the string.
export asm readStr()#1
+asmPlasmRet 0
bit setROM
jsr ROM_getln1
bit setLcRW+lcBank2
txa
pha
beq +
- lda inbuf-1,x
and #$7F
sta inbuf,x
dex
bne -
+ pla
sta inbuf,x
lda #<inbuf
ldy #>inbuf
rts
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Send a command to the memory manager // Send a command to the memory manager
export asm mmgr(cmd, wordParam)#1 export asm mmgr(cmd, wordParam)#1
@ -998,7 +971,8 @@ export asm streqi(a, b)#1
bne -- ; abort on inequality bne -- ; abort on inequality
dex dex
bne - bne -
ldy #0 ; okay, they're equal. Return non-zero (A is guaranteed to be a character already) lda #1
ldy #0 ; okay, they're equal. Return 1 (not just any char; so that PLASMA when...is can work)
rts rts
end end
@ -1098,12 +1072,6 @@ export def getStringResponse()#1
return mmgr(HEAP_INTERN, $200) return mmgr(HEAP_INTERN, $200)
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Setter functions for library use
export def setPlural(flg)#0
isPlural = flg
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Convert signed decimal to string in decimalBuf (@decimalBuf returned) // Convert signed decimal to string in decimalBuf (@decimalBuf returned)
def convertDec(n)#1 def convertDec(n)#1
@ -1230,14 +1198,6 @@ export def parseDec(str)#1
return n return n
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def parseDecWithDefault(str, default)#1
if ^str == 0
return default
fin
return parseDec(str)
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Get a keystroke and convert it to upper case // Get a keystroke and convert it to upper case
export def getUpperKey()#1 export def getUpperKey()#1
@ -1316,7 +1276,7 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def rollDiceWithLuck(encoded, luck)#1 export def rollDiceWithLuck(encoded, luck)#1
byte i, nDice, dieSize, add, droll, result byte i, nDice, dieSize, add, droll, result
nDice = encoded >> 12 nDice = (encoded >> 12) & $F // must mask off replicated hi-bits
dieSize = (encoded >> 8) & $F dieSize = (encoded >> 8) & $F
add = encoded & $F add = encoded & $F
result = add result = add
@ -1331,8 +1291,7 @@ export def rollDiceWithLuck(encoded, luck)#1
droll = min(droll, (rand16() % dieSize) + 1) droll = min(droll, (rand16() % dieSize) + 1)
fin fin
fin fin
add = (rand16() % dieSize) + 1 result = result + droll
result = result + add
next next
return result return result
end end
@ -1361,13 +1320,13 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Look up the partition for a resource. // Look up the partition for a resource.
// sectioNum: 1=map2d, 2=map3d, 3=portrait // sectioNum: 1=map2d, 2=map3d, 3=portrait
def lookupResourcePart(sectionNum, resourceNum)#1 export def lookupResourcePart(sectionNum, resourceNum)#1
word ptr word ptr
byte n byte n
// Skip to the requested section // Skip to the requested section (starting just after version num)
ptr = pResourceIndex ptr = pResourceIndex
while sectionNum > 1 while sectionNum > 0
ptr = ptr + readAuxByte(ptr) + 1 ptr = ptr + readAuxByte(ptr) + 1
sectionNum-- sectionNum--
loop loop
@ -1381,17 +1340,13 @@ def lookupResourcePart(sectionNum, resourceNum)#1
if curMapPartition > 0; return curMapPartition; fin if curMapPartition > 0; return curMapPartition; fin
return 2 return 2
fin fin
if n < 1 or n > 20; fatal("lkupFail2"); fin if n < 0 or n > 20; fatal("lkupFail2"); fin // allow zero (e.g. portrait not used so not packed)
return n return n
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Set the sky color (relevant to 3D display only) // Set the sky color (relevant to 3D display only)
export def setSky(num)#0 export def setSky(num)#0
// hack for end-game screen
if num == 99
finalWin()
fin
skyNum = num skyNum = num
setColor(0, skyNum) setColor(0, skyNum)
needRender = TRUE needRender = TRUE
@ -1405,6 +1360,36 @@ export def setGround(num)#0
needRender = TRUE needRender = TRUE
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def printVersion()#0
word p, len, cv, ch
if !pResourceIndex; return; fin
cv = ^$25
^$23 = 24 // full height window
^$25 = 22
crout()
^$24 = 25
puts("V ")
setWindow(183, 192, 161, 261)
clearWindow()
setWindow(183, 192, 168, 252)
rawDisplayStr("^YV ")
p = pResourceIndex
len = readAuxByte(p)
while len
p++
ch = readAuxByte(p)
printChar(ch)
displayChar(ch)
len--
loop
rawDisplayStr("^N")
^$23 = 23 // shrink window to protect version num
^$25 = cv-1
crout()
setWindow2()
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Load the Frame Image, and lock it. // Load the Frame Image, and lock it.
export def loadFrameImg(img)#0 export def loadFrameImg(img)#0
@ -1435,6 +1420,9 @@ export def loadFrameImg(img)#0
// And show the first frame of the screen image // And show the first frame of the screen image
showAnimFrame() showAnimFrame()
// Brand the image with the version number
printVersion()
else else
curFullscreenImg = NULL curFullscreenImg = NULL
anyAnims = FALSE anyAnims = FALSE
@ -1591,6 +1579,9 @@ export def scriptEvent(event, param)#0
word script word script
if !nMapScripts; return; fin if !nMapScripts; return; fin
if inScript; return; fin // avoid doing scripted events inside other scripts
inScript = TRUE
setWindow2() setWindow2()
textDrawn = FALSE textDrawn = FALSE
@ -1606,6 +1597,8 @@ export def scriptEvent(event, param)#0
fin fin
next next
inScript = FALSE
if textDrawn if textDrawn
textClearCountdown = 3 textClearCountdown = 3
if mapIs3D and texturesLoaded; copyWindow(); fin if mapIs3D and texturesLoaded; copyWindow(); fin
@ -1650,11 +1643,15 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def loadMainFrameImg()#0 export def loadMainFrameImg()#0
loadFrameImg(mapIs3D+2)
if curFullscreenImg if curFullscreenImg
auxMmgr(FREE_MEMORY, curFullscreenImg) auxMmgr(FREE_MEMORY, curFullscreenImg)
curFullscreenImg = NULL curFullscreenImg = NULL
fin fin
loadFrameImg(mapIs3D+2)
if curFullscreenImg
auxMmgr(FREE_MEMORY, curFullscreenImg) // we don't allow animated main frames, so save memory
curFullscreenImg = NULL
fin
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -1796,15 +1793,17 @@ def doRender()#0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Advance one step forward (works for either 3D or 2D maps) def moveInternal(facingDir, moveDir, beepOK)#1
def moveForward()#1
byte val byte val
word x, y word x, y
setDir(moveDir)
val = advance() val = advance()
setDir(facingDir)
// If not blocked, render at the new position. // If not blocked, render at the new position.
if val == 0 if val == 0
beep() if beepOK and !inScript; beep(); fin // don't beep for scripted moves
else else
if !mapIs3D if !mapIs3D
doRender() doRender()
@ -1813,7 +1812,7 @@ def moveForward()#1
fin fin
fin fin
// If we're on a new map tile, run leave handlers. // If we're on a new map tile, run leave handlers from old tile.
if val >= 2 if val >= 2
scriptEvent(@S_LEAVE, NULL) scriptEvent(@S_LEAVE, NULL)
nMapScripts = 0 nMapScripts = 0
@ -1825,14 +1824,22 @@ def moveForward()#1
scanScripts(x, y) scanScripts(x, y)
if nMapScripts if nMapScripts
scriptEvent(@S_ENTER, NULL) scriptEvent(@S_ENTER, NULL)
elsif global=>p_encounterZones and !skipEncounterCheck elsif global=>p_encounterZones
checkEncounter(x, y, FALSE) checkEncounter(x, y, FALSE)
fin fin
elsif val >= 2 and global=>p_encounterZones and !skipEncounterCheck elsif val >= 2 and global=>p_encounterZones
getPos(@x, @y) getPos(@x, @y)
checkEncounter(x, y, FALSE) checkEncounter(x, y, FALSE)
fin fin
return 0 return val
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Advance one step forward (works for either 3D or 2D maps)
def moveForward()#1
byte dir
dir = getDir()
return moveInternal(dir, dir, TRUE)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -1847,23 +1854,25 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Move backward one step (3D mode). Also actually works in 2D mode. // Move backward one step (3D mode). Also actually works in 2D mode.
def moveBackward()#1 def moveBackward()#1
adjustDir(8) byte facingDir, moveDir
moveForward() facingDir = getDir()
return adjustDir(8) moveDir = (facingDir + 8) & 15
moveInternal(facingDir, moveDir, TRUE)
return 0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Move backward two steps (3D mode), or one step (2D mode). This is often used when exiting a // Move backward two steps (3D mode), or one step (2D mode). This is often used when exiting a
// building or fleeing combat, so we don't want to generate any random encounters. // building or fleeing combat, so we don't want to generate any random encounters.
export def moveWayBackward()#1 export def moveWayBackward()#1
adjustDir(8) byte facingDir, moveDir
skipEncounterCheck = TRUE facingDir = getDir()
moveForward() moveDir = (facingDir + 8) & 15
moveInternal(facingDir, moveDir, FALSE)
if mapIs3D if mapIs3D
moveForward() moveInternal(facingDir, moveDir, FALSE)
fin fin
skipEncounterCheck = FALSE return 0
return adjustDir(8)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -1885,17 +1894,19 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Sidestep to the right (3D mode) // Sidestep to the right (3D mode)
def strafeRight()#1 def strafeRight()#1
adjustDir(4) byte facingDir, moveDir
moveForward() facingDir = getDir()
return adjustDir(-4) moveDir = (facingDir + 4) & 15
return moveInternal(facingDir, moveDir, FALSE)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Sidestep to the left (3D mode) // Sidestep to the left (3D mode)
def strafeLeft()#1 def strafeLeft()#1
adjustDir(-4) byte facingDir, moveDir
moveForward() facingDir = getDir()
return adjustDir(4) moveDir = (facingDir - 4) & 15
return moveInternal(facingDir, moveDir, FALSE)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2105,7 +2116,9 @@ export def getYN()#1
if key == 'Y' if key == 'Y'
return 1 return 1
elsif key == 'N' elsif key == 'N'
if frameLoaded
clearTextWindow() clearTextWindow()
fin
break break
fin fin
beep() beep()
@ -2185,7 +2198,8 @@ export def setPortrait(portraitNum)#0
// Load the portrait image and display it // Load the portrait image and display it
part = lookupResourcePart(3, portraitNum) part = lookupResourcePart(3, portraitNum)
if part > 1; part = curMapPartition; fin // Look on disk 1 or current disk only // Commented out below, because it prevents cycling thru all portraits (in god mode)
// NO: if part > 1; part = curMapPartition; fin // Look on disk 1 or current disk only
mmgr(START_LOAD, part) mmgr(START_LOAD, part)
curPortrait = auxMmgr(QUEUE_LOAD, portraitNum<<8 | RES_TYPE_PORTRAIT) curPortrait = auxMmgr(QUEUE_LOAD, portraitNum<<8 | RES_TYPE_PORTRAIT)
curPortraitNum = portraitNum curPortraitNum = portraitNum
@ -2303,12 +2317,6 @@ def loadEngine(moduleNum)#1
flipToPage1() flipToPage1()
mmgr(START_LOAD, 1) // code is in partition 1 mmgr(START_LOAD, 1) // code is in partition 1
curEngine = mmgr(QUEUE_LOAD, moduleNum<<8 | RES_TYPE_MODULE) curEngine = mmgr(QUEUE_LOAD, moduleNum<<8 | RES_TYPE_MODULE)
// For combat module, pre-load some global funcs
if moduleNum == MOD_COMBAT
mmgr(QUEUE_LOAD, GS_COMBAT_INTRO<<8 | RES_TYPE_MODULE)
mmgr(QUEUE_LOAD, GS_COMBAT_PROMPT<<8 | RES_TYPE_MODULE)
mmgr(QUEUE_LOAD, GS_ENEMY_INTRO<<8 | RES_TYPE_MODULE)
fin
mmgr(FINISH_LOAD, 0) mmgr(FINISH_LOAD, 0)
return curEngine() // return function table return curEngine() // return function table
end end
@ -2323,7 +2331,12 @@ def returnFromEngine(render)#0
if renderLoaded; texControl(1); texturesLoaded = TRUE; fin if renderLoaded; texControl(1); texturesLoaded = TRUE; fin
mapNameHash = 0; showMapName(global=>s_mapName) mapNameHash = 0; showMapName(global=>s_mapName)
clearTextWindow() clearTextWindow()
if render; doRender(); fin if render
doRender()
else
needRender = TRUE
fin
if mapIs3D; showCompassDir(getDir()); fin
showParty() showParty()
setWindow2() // in case we're mid-script setWindow2() // in case we're mid-script
fin fin
@ -2357,13 +2370,21 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Load the Party engine and show data for the given player // Load the Party engine and show data for the given player
def showPlayerSheet(num)#1 def showPlayerSheet(num)#1
word pItemToUse word pItemToUse, oldFlg
if num+1 > countList(global=>p_players); beep; return 0; fin if num+1 > countList(global=>p_players); beep; return 0; fin
pItemToUse = loadEngine(MOD_PARTY)=>party_showPlayerSheet(num) pItemToUse = loadEngine(MOD_PARTY)=>party_showPlayerSheet(num)
returnFromEngine(TRUE) returnFromEngine(TRUE)
// General 'use' handled here in case it triggers graphical effects // General 'use' handled here in case it triggers graphical effects
if pItemToUse if pItemToUse
oldFlg = textDrawn
textDrawn = FALSE
scriptEvent(@S_USE, pItemToUse=>s_name) scriptEvent(@S_USE, pItemToUse=>s_name)
if !textDrawn
displayStr("\nNothing happened.")
textDrawn = TRUE
else
textDrawn = oldFlg
fin
fin fin
return 0 return 0
end end
@ -2391,6 +2412,7 @@ def levelUp()#1
return 0 return 0
fin fin
player = player=>p_nextObj player = player=>p_nextObj
n++
loop loop
beep beep
return 0 return 0
@ -2433,13 +2455,14 @@ def doCombat(mapCode, backUpOnFlee)#1
// Handled in a separate module. Clear enemies out of the heap when finished. // Handled in a separate module. Clear enemies out of the heap when finished.
result = loadEngine(MOD_COMBAT)=>combat_zoneEncounter(mapCode) result = loadEngine(MOD_COMBAT)=>combat_zoneEncounter(mapCode)
global=>p_enemyGroups = NULL global=>p_enemyGroups = NULL
mmgr(CHECK_MEM, 0)
mmgr(HEAP_COLLECT, 0) mmgr(HEAP_COLLECT, 0)
if (result == -99) if (result == -99)
playerDeath() playerDeath()
return 0 return 0
fin fin
returnFromEngine(TRUE) returnFromEngine(!inScript) // only re-render if outside script
// If the party fled the combat instead of winning, back up to previous square. // If the party fled the combat instead of winning, back up to previous square.
if !result and backUpOnFlee if !result and backUpOnFlee
@ -2455,6 +2478,9 @@ export def checkEncounter(x, y, force)#0
word p_bestZone, bestDist word p_bestZone, bestDist
word d word d
// Don't check for encounter during scripted move
if inScript; return; fin
// Find the zone that's closest, but not too far. // Find the zone that's closest, but not too far.
bestDist = INT_MAX bestDist = INT_MAX
p_bestZone = NULL p_bestZone = NULL
@ -2511,6 +2537,7 @@ def toggleGodMode()#1
if ^kbd == $84 // ctrl-D if ^kbd == $84 // ctrl-D
^kbdStrobe ^kbdStrobe
global->b_godmode = !global->b_godmode global->b_godmode = !global->b_godmode
flipToPage1()
clearTextWindow() clearTextWindow()
displayf1("gm:%d\n", global->b_godmode & 1) displayf1("gm:%d\n", global->b_godmode & 1)
beep; beep beep; beep
@ -2521,15 +2548,6 @@ def toggleGodMode()#1
return 0 return 0
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def finalWin()#0
flipToPage1()
loadFrameImg(4) // total hack
while 1 // 1 infinite loop
getUpperKey()
loop
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def setCmd(key, func)#0 export def setCmd(key, func)#0
cmdTbl[key] = func cmdTbl[key] = func
@ -2641,10 +2659,6 @@ def loadTitle()#0
auxMmgr(SET_MEM_TARGET, expandVec) auxMmgr(SET_MEM_TARGET, expandVec)
auxMmgr(QUEUE_LOAD, CODE_EXPAND<<8 | RES_TYPE_CODE) auxMmgr(QUEUE_LOAD, CODE_EXPAND<<8 | RES_TYPE_CODE)
// Also grab the resource index (put it in aux)
pResourceIndex = auxMmgr(QUEUE_LOAD, CODE_RESOURCE_INDEX<<8 | RES_TYPE_CODE)
auxMmgr(LOCK_MEMORY, pResourceIndex)
mmgr(FINISH_LOAD, 0) mmgr(FINISH_LOAD, 0)
// Tell the font engine where to find its font // Tell the font engine where to find its font
@ -2663,13 +2677,20 @@ def loadTitle()#0
auxMmgr(SET_MEM_TARGET, expandVec) auxMmgr(SET_MEM_TARGET, expandVec)
auxMmgr(REQUEST_MEMORY, expanderSize) auxMmgr(REQUEST_MEMORY, expanderSize)
auxMmgr(LOCK_MEMORY, expandVec) auxMmgr(LOCK_MEMORY, expandVec)
// To reduce fragmentation, load the resource index directly after the
// remaining part of the expander
pResourceIndex = expandVec + expanderSize
auxMmgr(SET_MEM_TARGET, pResourceIndex)
auxMmgr(QUEUE_LOAD, CODE_RESOURCE_INDEX<<8 | RES_TYPE_CODE)
auxMmgr(LOCK_MEMORY, pResourceIndex)
mmgr(FINISH_LOAD, 0)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Set up the small-object heap. Set loadedSize to zero on initial, or non-zero for loaded game. // Set up the small-object heap. Set loadedSize to zero on initial, or non-zero for loaded game.
export def initHeap(loadedSize)#0 export def initHeap(loadedSize)#0
byte i byte i
word typeHash
if !heapLocked if !heapLocked
mmgr(SET_MEM_TARGET, HEAP_BOTTOM) mmgr(SET_MEM_TARGET, HEAP_BOTTOM)
@ -2687,12 +2708,8 @@ export def initHeap(loadedSize)#0
mmgr(HEAP_ADD_TYPE, typeTbls[i]) mmgr(HEAP_ADD_TYPE, typeTbls[i])
i = i+1 i = i+1
loop loop
typeHash = hashBuffer(@typeTbl_Global, @typeTbls - @typeTbl_Global) ^ HEAP_BOTTOM
if loadedSize <> 0 if loadedSize <> 0
global = HEAP_BOTTOM global = HEAP_BOTTOM
if global=>w_typeHash <> typeHash
fatal("Incompatible saved game")
fin
else else
global = mmgr(HEAP_ALLOC, TYPE_GLOBAL) global = mmgr(HEAP_ALLOC, TYPE_GLOBAL)
global=>w_typeHash = typeHash global=>w_typeHash = typeHash
@ -2763,12 +2780,23 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Add to a list if named obj not already in it; returns TRUE if really added. // Add to a list if named obj not already in it; returns TRUE if really added.
// Also handles incrementing stackable items.
export def addUnique(pList, p_thing)#1 export def addUnique(pList, p_thing)#1
if !scanForNamedObj(*pList, p_thing=>s_name) word p_existing
addToList(pList, p_thing)
// If it's stackable and player already has some, just increase the stack
p_existing = scanForNamedObj(*pList, p_thing=>s_name)
if p_existing
if p_existing->t_type == TYPE_FANCY_ITEM and p_existing=>w_count > 0 and !p_existing=>p_modifiers
p_existing=>w_count = p_existing=>w_count + p_thing=>w_count
return TRUE return TRUE
fin fin
return FALSE return FALSE // already have one, and it's not stackable
fin
// Otherwise, add a new item
addToList(pList, p_thing)
return TRUE
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2868,7 +2896,8 @@ end
export def addPlayerToParty(playerFuncNum)#0 export def addPlayerToParty(playerFuncNum)#0
word p word p
if countList(global=>p_players) == MAX_PARTY if countList(global=>p_players) == MAX_PARTY
displayStr("Party too large.") rawDisplayStr("Party too large.")
beep
return return
fin fin
p = createAndAddUnique(MOD_GEN_PLAYERS, playerFuncNum, @global=>p_players) p = createAndAddUnique(MOD_GEN_PLAYERS, playerFuncNum, @global=>p_players)
@ -2882,7 +2911,12 @@ export def removeNamed(name, pList)#0
word p_thing word p_thing
p_thing = scanForNamedObj(*pList, name) p_thing = scanForNamedObj(*pList, name)
if p_thing if p_thing
// If it's stackable and there's more than one, just reduce the count. Otherwise take it all.
if p_thing->t_type == TYPE_FANCY_ITEM and p_thing=>w_count > 1
p_thing=>w_count--
else
removeFromList(pList, p_thing) removeFromList(pList, p_thing)
fin
else else
printf1("Warning: couldn't find '%s' to remove.\n", name) printf1("Warning: couldn't find '%s' to remove.\n", name)
fin fin
@ -2912,29 +2946,29 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def getStat(player, statName)#1 export def getStat(player, statName)#1
word pSkill word pSkill
when statName when 1
is @S_INTELLIGENCE; return player->b_intelligence is streqi(statName, @S_INTELLIGENCE); return player->b_intelligence
is @S_STRENGTH; return player->b_strength is streqi(statName, @S_STRENGTH); return player->b_strength
is @S_AGILITY; return player->b_agility is streqi(statName, @S_AGILITY); return player->b_agility
is @S_STAMINA; return player->b_stamina is streqi(statName, @S_STAMINA); return player->b_stamina
is @S_CHARISMA; return player->b_charisma is streqi(statName, @S_CHARISMA); return player->b_charisma
is @S_SPIRIT; return player->b_spirit is streqi(statName, @S_SPIRIT); return player->b_spirit
is @S_LUCK; return player->b_luck is streqi(statName, @S_LUCK); return player->b_luck
is @S_HEALTH; return player=>w_health is streqi(statName, @S_HEALTH); return player=>w_health
is @S_MAX_HEALTH; return player=>w_maxHealth is streqi(statName, @S_MAX_HEALTH); return player=>w_maxHealth
is @S_AIMING; return player->b_aiming is streqi(statName, @S_AIMING); return player->b_aiming
is @S_HAND_TO_HAND; return player->b_handToHand is streqi(statName, @S_HAND_TO_HAND); return player->b_handToHand
is @S_DODGING; return player->b_dodging is streqi(statName, @S_DODGING); return player->b_dodging
is @S_GOLD; return global=>w_gold is streqi(statName, @S_GOLD); return global=>w_gold
is @S_XP; return player=>w_curXP is streqi(statName, @S_XP); return player=>w_curXP
is @S_SP; return player->b_skillPoints is streqi(statName, @S_SP); return player->b_skillPoints
wend wend
pSkill = player=>p_skills pSkill = player=>p_skills
while pSkill while pSkill
if streqi(statName, pSkill=>s_name); return pSkill=>w_modValue; fin if streqi(statName, pSkill=>s_name); return pSkill=>w_modValue; fin
pSkill = pSkill=>p_nextObj pSkill = pSkill=>p_nextObj
loop loop
puts(statName); return fatal("Unknown stat") puts(statName); return fatal("getStat")
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2953,22 +2987,22 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def setStat(player, statName, val)#0 export def setStat(player, statName, val)#0
word pSkill word pSkill
when statName when 1
is @S_INTELLIGENCE; player->b_intelligence = clampByte(val); break is streqi(statName, @S_INTELLIGENCE); player->b_intelligence = clampByte(val); break
is @S_STRENGTH; player->b_strength = clampByte(val); break is streqi(statName, @S_STRENGTH); player->b_strength = clampByte(val); break
is @S_AGILITY; player->b_agility = clampByte(val); break is streqi(statName, @S_AGILITY); player->b_agility = clampByte(val); break
is @S_STAMINA; player->b_stamina = clampByte(val); break is streqi(statName, @S_STAMINA); player->b_stamina = clampByte(val); break
is @S_CHARISMA; player->b_charisma = clampByte(val); break is streqi(statName, @S_CHARISMA); player->b_charisma = clampByte(val); break
is @S_SPIRIT; player->b_spirit = clampByte(val); break is streqi(statName, @S_SPIRIT); player->b_spirit = clampByte(val); break
is @S_LUCK; player->b_luck = clampByte(val); break is streqi(statName, @S_LUCK); player->b_luck = clampByte(val); break
is @S_HEALTH; player=>w_health = max(0, min(player=>w_maxHealth, val)); needShowParty = TRUE; break is streqi(statName, @S_HEALTH); player=>w_health = max(0, min(player=>w_maxHealth, val)); needShowParty = TRUE; break
is @S_MAX_HEALTH; player=>w_maxHealth = max(0, val); break is streqi(statName, @S_MAX_HEALTH); player=>w_maxHealth = max(0, val); break
is @S_AIMING; player->b_aiming = clampByte(val); break is streqi(statName, @S_AIMING); player->b_aiming = clampByte(val); break
is @S_HAND_TO_HAND; player->b_handToHand = clampByte(val); break is streqi(statName, @S_HAND_TO_HAND); player->b_handToHand = clampByte(val); break
is @S_DODGING; player->b_dodging = clampByte(val); break is streqi(statName, @S_DODGING); player->b_dodging = clampByte(val); break
is @S_GOLD; global=>w_gold = max(0, val); needShowParty = TRUE; break is streqi(statName, @S_GOLD); global=>w_gold = max(0, val); needShowParty = TRUE; break
is @S_XP; player=>w_curXP = max(player=>w_curXP, val); needShowParty = TRUE; break is streqi(statName, @S_XP); player=>w_curXP = max(player=>w_curXP, val); needShowParty = TRUE; break
is @S_SP; player->b_skillPoints = max(0, val); needShowParty = TRUE; break is streqi(statName, @S_SP); player->b_skillPoints = max(0, val); needShowParty = TRUE; break
otherwise otherwise
pSkill = player=>p_skills pSkill = player=>p_skills
while pSkill while pSkill
@ -2978,33 +3012,28 @@ export def setStat(player, statName, val)#0
fin fin
pSkill = pSkill=>p_nextObj pSkill = pSkill=>p_nextObj
loop loop
puts(statName); fatal("Unknown stat") puts(statName); fatal("setStat")
wend wend
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def setGameFlag(flagName, val)#0 export def setGameFlag(flagNum, val)#0
word p_flag byte byteNum, mask
p_flag = scanForNamedObj(global=>p_gameFlags, flagName) byteNum = flagNum >> 3
if p_flag mask = 1<<(flagNum & 7)
if val == 0 // setting flag to zero removes it if val
removeFromList(@global=>p_gameFlags, p_flag) global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] | mask
else else
p_flag=>w_modValue = val global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] & ~mask
fin
elsif val <> 0
addToList(@global=>p_gameFlags, makeModifier(flagName, val))
fin fin
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
export def getGameFlag(flagName)#1 export def getGameFlag(flagNum)#1
word p_flag byte byteNum, mask
p_flag = scanForNamedObj(global=>p_gameFlags, flagName) byteNum = flagNum >> 3
if p_flag mask = 1<<(flagNum & 7)
return p_flag=>w_modValue return global->ba_gameFlags[byteNum] & mask
fin
return 0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -3044,10 +3073,23 @@ export def buySell(storeCode, profitRatio)#0
if portrait; setPortrait(portrait); fin if portrait; setPortrait(portrait); fin
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def numToPlayer(num)#1
word player
player = global=>p_players
while player and num > 0
player = player=>p_nextObj
num--
loop
return player
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def startGame(ask)#0 def startGame(ask)#0
word p_module word p_module
typeHash = hashBuffer(@typeTbl_Global, @typeTbls - @typeTbl_Global) ^ HEAP_BOTTOM
// Create a new game or load an existing one // Create a new game or load an existing one
mmgr(START_LOAD, 1) // code is in partition 1 mmgr(START_LOAD, 1) // code is in partition 1
p_module = mmgr(QUEUE_LOAD, MOD_DISKOPS<<8 | RES_TYPE_MODULE) p_module = mmgr(QUEUE_LOAD, MOD_DISKOPS<<8 | RES_TYPE_MODULE)

View File

@ -16,6 +16,7 @@ include "gen_images.plh"
include "gen_modules.plh" include "gen_modules.plh"
include "gen_items.plh" include "gen_items.plh"
include "gen_players.plh" include "gen_players.plh"
include "gen_flags.plh"
// Exported functions go here. First a predef for each one, then a table with function pointers // Exported functions go here. First a predef for each one, then a table with function pointers
// in the same order as the constants are defined in the the header. // in the same order as the constants are defined in the the header.
@ -24,6 +25,56 @@ predef _addItem(player)#1
predef _addPlayer()#1 predef _addPlayer()#1
word[] funcTbl = @_setCheatCmds, @_addItem, @_addPlayer word[] funcTbl = @_setCheatCmds, @_addItem, @_addPlayer
asm _defs
; Use hi-bit ASCII for Apple II
!convtab "../../include/hiBitAscii.ct"
; Headers
!source "../../include/global.i"
!source "../../include/plasma.i"
!source "../../include/mem.i"
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Read a string from the keyboard, turn it into a PLASMA string and return a pointer to the string.
asm readStr()#1
+asmPlasmRet 0
bit setROM
jsr ROM_getln1
bit setLcRW+lcBank2
txa
pha
beq +
- lda inbuf-1,x
and #$7F
sta inbuf,x
dex
bne -
+ pla
sta inbuf,x
lda #<inbuf
ldy #>inbuf
rts
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def parseDecWithDefault(str, default)#1
if ^str == 0
return default
fin
return parseDec(str)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def splitScreenMode()#0
flipToPage1()
textHome()
^$c053
^$25 = 19
crout()
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Godmode cheats // Godmode cheats
@ -32,12 +83,10 @@ def kbdTeleport()#1
word x, y word x, y
byte dir byte dir
flipToPage1() splitScreenMode()
^$c053
if ^$25 < 23; ^$25 = 23; fin
getPos(@x, @y) getPos(@x, @y)
dir = getDir() dir = getDir()
printf3("\nCurrent: X=%d Y=%d Facing=%d\n", x, y, dir) printf3("Current: X=%d Y=%d Facing=%d\n", x, y, dir)
printf1("3D [%d]: ", mapIs3D) printf1("3D [%d]: ", mapIs3D)
d3 = parseDecWithDefault(readStr(), mapIs3D) d3 = parseDecWithDefault(readStr(), mapIs3D)
@ -62,11 +111,9 @@ def showPos()#1
word x, y word x, y
byte dir byte dir
flipToPage1() splitScreenMode()
^$c053
if ^$25 < 23; ^$25 = 23; fin
getPos(@x, @y) getPos(@x, @y)
printf2("\nX=%d Y=%d ", x, y) printf2("X=%d Y=%d ", x, y)
if mapIs3D if mapIs3D
printf3("Facing=%d Sky=%d Ground=%d", getDir(), skyNum, groundNum) printf3("Facing=%d Sky=%d Ground=%d", getDir(), skyNum, groundNum)
fin fin
@ -77,19 +124,28 @@ def showPos()#1
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def nextPortrait()#1 def advPortrait(dir)#1
portraitNum = portraitNum + 1 while TRUE
if portraitNum > PO_LAST; portraitNum = 1; fin portraitNum = portraitNum + dir
if portraitNum > PO_LAST
portraitNum = 1
elsif portraitNum < 1
portraitNum = PO_LAST
fin
if lookupResourcePart(3, portraitNum); break; fin
loop
setPortrait(portraitNum) setPortrait(portraitNum)
return 0 return 0
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def nextPortrait()#1
return advPortrait(1)
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def prevPortrait()#1 def prevPortrait()#1
portraitNum = portraitNum - 1 return advPortrait(-1)
if portraitNum < 1; portraitNum = PO_LAST; fin
setPortrait(portraitNum)
return 0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -126,6 +182,45 @@ def nextGround()#1
return 0 return 0
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Edit flags cheat
def editFlags()#1
word pModule, funcTbl, flagName, flagNum
flipToPage1
^$c051
textHome()
*$4000 = 0 // so renderer knows we messed up the page
puts("Game flags:")
mmgr(START_LOAD, 1) // code is in partition 1
pModule = mmgr(QUEUE_LOAD, MOD_GEN_FLAGS<<8 | RES_TYPE_MODULE)
mmgr(FINISH_LOAD, 0)
funcTbl = pModule()
while TRUE
^$25 = 0
crout()
for flagNum = 0 to NUM_GAME_FLAGS-1
if flagNum > 0; puts(", "); fin
flagName = funcTbl=>flags_nameForNumber(flagNum)
// make sure flag name will fit on line
if ^$24 + ^flagName > 37; crout(); fin
^$32 = $FF // normal mode
if getGameFlag(flagNum); ^$32 = $3F; fin // inverse mode
puts(flagName)
^$32 = $FF
next
puts("\nToggle: ")
^$24 = 8
flagName = readStr()
if !^flagName; break; fin
flagNum = funcTbl=>flags_numberForName(flagName)
if flagNum < 0; beep; continue; fin
setGameFlag(flagNum, !getGameFlag(flagNum))
loop
^$c050
mmgr(FREE_MEMORY, pModule)
return 0
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def _setCheatCmds()#1 def _setCheatCmds()#1
// install cheat commands // install cheat commands
@ -137,7 +232,7 @@ def _setCheatCmds()#1
setCmd('Y', @nextSky) setCmd('Y', @nextSky)
setCmd('G', @nextGround) setCmd('G', @nextGround)
setCmd('&', @printMem) setCmd('&', @printMem)
setCmd('_', @finalWin) setCmd('^', @editFlags)
return 0 return 0
end end
@ -176,10 +271,14 @@ def selectThing(moduleNum, nThings, nSkip, prompt)#1
nFunc = -1 nFunc = -1
textHome() textHome()
fin fin
elsif moduleNum == MOD_GEN_PLAYERS
// Players are big, so need collect each time
mmgr(HEAP_COLLECT, 0)
fin fin
next next
mmgr(FREE_MEMORY, pModule) mmgr(FREE_MEMORY, pModule)
^$c050
return nFunc return nFunc
end end
@ -188,11 +287,7 @@ end
def _addItem(player)#1 def _addItem(player)#1
word funcNum word funcNum
funcNum = selectThing(MOD_GEN_ITEMS, NUM_ITEMS, 2, "Add item #: ") funcNum = selectThing(MOD_GEN_ITEMS, NUM_ITEMS, 2, "Add item #: ")
if funcNum >= 0 if funcNum >= 0; giveItemToPlayer(player, funcNum); fin
giveItemToPlayer(player, funcNum)
rdkey()
fin
^$c050
return 0 return 0
end end
@ -201,11 +296,7 @@ end
def _addPlayer()#1 def _addPlayer()#1
word funcNum word funcNum
funcNum = selectThing(MOD_GEN_PLAYERS, NUM_PLAYERS, 1, "Add player #: ") funcNum = selectThing(MOD_GEN_PLAYERS, NUM_PLAYERS, 1, "Add player #: ")
if funcNum >= 0 if funcNum >= 0; addPlayerToParty(funcNum); fin
addPlayerToParty(funcNum)
rdkey()
fin
^$c050
return 0 return 0
end end

View File

@ -68,6 +68,14 @@ def travFind2(pObj, val, func)
return NULL return NULL
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def isEquipped(pItem)#1
if pItem->t_type == TYPE_ARMOR or pItem->t_type == TYPE_WEAPON
return pItem->b_flags & ITEM_FLAG_EQUIP
fin
return FALSE
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def itemByNum(player, num)#1 def itemByNum(player, num)#1
return travFind2(player=>p_items, num, &(n,p)(n-1, n==0)) return travFind2(player=>p_items, num, &(n,p)(n-1, n==0))
@ -81,7 +89,7 @@ def unequip(player, type, kind)#1
item = player=>p_items item = player=>p_items
while item while item
if item->t_type == type if item->t_type == type
if (streqi(item=>s_itemKind, kind) or type == TYPE_WEAPON) and item->b_flags & ITEM_FLAG_EQUIP if (streqi(item=>s_itemKind, kind) or type == TYPE_WEAPON) and isEquipped(item)
item->b_flags = item->b_flags & ~ITEM_FLAG_EQUIP item->b_flags = item->b_flags & ~ITEM_FLAG_EQUIP
break break
fin fin
@ -97,7 +105,7 @@ end
def showColumnTitle(x, title, page, nPages)#0 def showColumnTitle(x, title, page, nPages)#0
rawDisplayf2("^V000\n^J^J^L^J^T%D%s", x, title) rawDisplayf2("^V000\n^J^J^L^J^T%D%s", x, title)
if nPages > 1 if nPages > 1
rawDisplayf2(" - p.%d/%d", page, nPages) rawDisplayf2(" p.%d/%d", page, nPages)
fin fin
rawDisplayStr("^N\n^J^J") rawDisplayStr("^N\n^J^J")
end end
@ -125,11 +133,9 @@ def showInventory(player, page, select)#1
if !first; displayChar('\n'); fin if !first; displayChar('\n'); fin
first = FALSE first = FALSE
if item->t_type == TYPE_WEAPON or item->t_type == TYPE_ARMOR if isEquipped(item)
if item->b_flags & ITEM_FLAG_EQUIP
rawDisplayStr("*") rawDisplayStr("*")
fin fin
fin
rawDisplayf2("^T%D%c.", INVLBL_X, 'A' + s_item) rawDisplayf2("^T%D%c.", INVLBL_X, 'A' + s_item)
s_item++ s_item++
@ -142,17 +148,6 @@ def showInventory(player, page, select)#1
return s_item return s_item
end end
def numToPlayer(num)#1
word player
player = global=>p_players
while num > 0
player = player=>p_nextObj
if !player; break; fin // Not that many players
num--
loop
return player
end
def displayDice(dice)#0 def displayDice(dice)#0
byte n, d, p byte n, d, p
n = (dice >> 12) & $0F n = (dice >> 12) & $0F
@ -196,7 +191,7 @@ def showDerived(player)#0
// Get weapon // Get weapon
weapon = player=>p_items weapon = player=>p_items
while weapon while weapon
if weapon->t_type == TYPE_WEAPON and weapon->b_flags & ITEM_FLAG_EQUIP; break; fin if weapon->t_type == TYPE_WEAPON and isEquipped(weapon); break; fin
weapon = weapon=>p_nextObj weapon = weapon=>p_nextObj
loop loop
@ -339,12 +334,8 @@ def showInvMenu(player, totalItems, itemPage, itemsOnPage)#0
rawDisplayf1("Item [A-%c], ", itemsOnPage-1+'A') rawDisplayf1("Item [A-%c], ", itemsOnPage-1+'A')
if totalItems > INV_ROWS if totalItems > INV_ROWS
rawDisplayStr("Pg [") rawDisplayStr("Pg [")
if totalItems > (itemPage + 1) * INV_ROWS if totalItems > (itemPage + 1) * INV_ROWS; rawDisplayStr(">"); fin
rawDisplayStr(">") if itemPage; rawDisplayStr("<"); fin
fin
if itemPage
rawDisplayStr("<")
fin
rawDisplayStr("], ") rawDisplayStr("], ")
fin fin
fin fin
@ -372,18 +363,37 @@ def showSkillsMenu()#0
rawDisplayStr("X:Inv or [Esc]") rawDisplayStr("X:Inv or [Esc]")
end end
def isSplittable(item)#1
// Disallow splitting items with modifiers, because too edge-casey
return item->t_type == TYPE_FANCY_ITEM and item=>w_count > 1 and !item=>p_modifiers
end
def isJoinable(item)#1
// Disallow joining items with modifiers, because too edge-casey
return item->t_type == TYPE_FANCY_ITEM and item=>w_count > 0 and !item=>p_modifiers
end
// Display menu for selecting inventory items // Display menu for selecting inventory items
def showItemMenu(item)#0 def showItemMenu(item)#0
byte type byte type
clearMenuRect() clearMenuRect()
type = item->t_type type = item->t_type
if type == TYPE_ARMOR or type == TYPE_WEAPON if type == TYPE_ARMOR or type == TYPE_WEAPON
rawDisplayStr("E)quip/unequip, ") if isEquipped(item)
rawDisplayStr("U)nequip, ")
else
rawDisplayStr("E)quip, ")
fin fin
if type == TYPE_ITEM elsif type == TYPE_PLAIN_ITEM or type == TYPE_FANCY_ITEM
rawDisplayStr("U)se, ") rawDisplayStr("U)se, ")
if isSplittable(item); rawDisplayStr("S)plit, "); fin
if isJoinable(item); rawDisplayStr("J)oin, "); fin
fin fin
rawDisplayStr("D)estroy or [Esc]") if !isEquipped(item)
if global=>p_players=>p_nextObj; rawDisplayStr("T)rade, "); fin
rawDisplayStr("D)rop ")
fin
rawDisplayStr("or [Esc]")
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -398,6 +408,7 @@ def clearMainRect()#0
setWindow(BIGWIN_TOP+9, BIGWIN_BOTTOM-10, BIGWIN_LEFT, BIGWIN_RIGHT) setWindow(BIGWIN_TOP+9, BIGWIN_BOTTOM-10, BIGWIN_LEFT, BIGWIN_RIGHT)
clearWindow() clearWindow()
setBigWindow() setBigWindow()
rawDisplayStr("^V000\n^J^J^J")
end end
// Equip/unequip an item. // Equip/unequip an item.
@ -408,26 +419,151 @@ def doEquip(player, item)#0
calcPlayerArmor(player) calcPlayerArmor(player)
end end
// Select an item and use it. Returns item if it needs to be processed by outer loop, else NULL def choosePlayer(disp, chooseNum, avoid)
def doUse(player, item)#1 word player
if item=>p_modifiers byte num
if streqi(item=>p_modifiers=>s_name, @S_HEALTH) player = global=>p_players
num = 0
while player
if player <> avoid
if num; rawDisplayStr(", "); fin
if disp
rawDisplayf2("%c) %s", num+'A', player=>s_name)
elsif num == chooseNum
return player
fin
num++
fin
player = player=>p_nextObj
loop
return num
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayDone()#1
clearMenuRect() clearMenuRect()
clearMainRect() clearMainRect()
rawDisplayStr("^V000\n^J^J^J") rawDisplayStr("Done.")
if player=>w_health < player=>w_maxHealth
player=>w_health = min(player=>w_health + item=>p_modifiers=>w_modValue, player=>w_maxHealth)
item->b_curUses++
if item->b_curUses >= item->b_maxUses // all used up
removeFromList(@player=>p_items, item)
fin
rawDisplayf2("Healed to %d/%d", player=>w_health, player=>w_maxHealth)
else
rawDisplayStr("No healing needed.")
fin
pause(800) pause(800)
return NULL return NULL
end
// Trade an item to another player/npc
def doTrade(player, item)#1
word destPlayer
byte first, sel
clearMenuRect()
rawDisplayStr("To: ")
destPlayer = global=>p_players
sel = 0
first = TRUE
while destPlayer
if player <> destPlayer
if !first; rawDisplayStr(", "); fin
first = FALSE
rawDisplayf2("%d. %s", sel+1, destPlayer=>s_name)
fin fin
sel++
destPlayer = destPlayer=>p_nextObj
loop
rawDisplayStr(" or [Esc]")
while TRUE
sel = getUpperKey()
if sel == $1B; return 0; fin
destPlayer = numToPlayer(sel-'1')
if destPlayer and destPlayer <> player
removeFromList(@player=>p_items, item)
addUnique(@destPlayer=>p_items, item)
return displayDone
fin
beep
loop
return 0
end
// Split a stack of stackables
def doSplit(player, item)#1
word nToSplit, newItem
if item=>w_count == 2
nToSplit = 1
else
clearMenuRect
rawDisplayf1("Split off 1-%d: ", item=>w_count - 1)
nToSplit = parseDec(getStringResponse())
fin
if nToSplit
item=>w_count = item=>w_count - nToSplit
newItem = mmgr(HEAP_ALLOC, TYPE_FANCY_ITEM)
memcpy(item, newItem, FancyItem)
newItem=>w_count = nToSplit
newItem=>p_nextObj = item=>p_nextObj
item=>p_nextObj = newItem
return 1
fin
beep
return 0
end
// Join a stack of stackables
def doJoin(player, item)#1
word match, pPrev
byte anyJoined
pPrev = @player=>p_items
match = player=>p_items
anyJoined = FALSE
while match
if match <> item and match->t_type == TYPE_FANCY_ITEM
if streqi(match=>s_name, item=>s_name)
item=>w_count = item=>w_count + match=>w_count
*pPrev = match=>p_nextObj
match = match=>p_nextObj
anyJoined = TRUE
continue
fin
fin
pPrev = @match=>p_nextObj
match = *pPrev
loop
if anyJoined
displayDone()
return 1
fin
clearMenuRect()
rawDisplayStr("No joinable stack found.")
beep
pause(800)
return 0
end
// Select an item and use it. Returns item if it needs to be processed by outer loop, else NULL
def doUse(player, item)#1
word pMod, oldVal, newVal
if item->t_type == TYPE_FANCY_ITEM and item=>p_modifiers
clearMenuRect()
clearMainRect()
pMod = item=>p_modifiers
while pMod
oldVal = getStat(player, pMod=>s_name)
setStat(player, pMod=>s_name, oldVal + pMod=>w_modValue)
newVal = getStat(player, pMod=>s_name)
rawDisplayStr(pMod=>s_name)
if newVal <> oldVal
takeItemFromPlayer(player, item=>s_name) // also handles reducing count of stackables
if newVal > oldVal
rawDisplayStr(" increased")
else
rawDisplayStr(" decreased")
fin
rawDisplayf2(" from %d to %d.", oldVal, newVal)
else
rawDisplayStr(" already at the limit.")
fin
pause(800)
pMod = pMod=>p_nextObj
loop
return NULL
fin fin
return item // general 'use' handled by outer engine, because it might involve graphics return item // general 'use' handled by outer engine, because it might involve graphics
end end
@ -435,7 +571,7 @@ end
// Select an item and drop it. Returns TRUE if anything changed // Select an item and drop it. Returns TRUE if anything changed
def doDestroy(player, item)#1 def doDestroy(player, item)#1
clearMenuRect() clearMenuRect()
rawDisplayStr("Destroy ") rawDisplayStr("Drop ")
_displayItemName(item) _displayItemName(item)
rawDisplayStr(" (Y/N)?") rawDisplayStr(" (Y/N)?")
if getYN() if getYN()
@ -451,13 +587,11 @@ def matchEquipped(player, match)#1
word item word item
item = player=>p_items item = player=>p_items
while item while item
if (item->t_type == match->t_type) and (item->t_type == TYPE_WEAPON or item->t_type == TYPE_ARMOR) if item->t_type == match->t_type and item <> match and isEquipped(item)
if item <> match and (item->b_flags & ITEM_FLAG_EQUIP)
if item->t_type <> TYPE_ARMOR or (item=>s_itemKind == match=>s_itemKind) if item->t_type <> TYPE_ARMOR or (item=>s_itemKind == match=>s_itemKind)
return item return item
fin fin
fin fin
fin
item = item=>p_nextObj item = item=>p_nextObj
loop loop
return NULL return NULL
@ -466,7 +600,7 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displayItems(pItem1, pItem2)#0 def displayItems(pItem1, pItem2)#0
clearMainRect() clearMainRect()
rawDisplayf1("^V000\n^J^J^L^J^T%DInventory", STATS_COL_1) rawDisplayf1("^T%DInventory", STATS_COL_1)
if pItem2 if pItem2
rawDisplayf1("^T%DEquipped", STATS_COL_2) rawDisplayf1("^T%DEquipped", STATS_COL_2)
fin fin
@ -477,37 +611,69 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def interactWithItem(player, item)#1 def interactWithItem(player, item)#1
word comp, quantity word comp, quantity
byte sel byte sel, ok
while TRUE ok = TRUE
displayItems(item, matchEquipped(player, item)) displayItems(item, matchEquipped(player, item))
while TRUE
if ok
showItemMenu(item) showItemMenu(item)
sel = getUpperKey()
rawDisplayf1(" %c\n", sel)
when sel
// Equip/unequip player with weapon/armor
is 'E'
if item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON
doEquip(player, item)
return NULL
else else
beep beep
fin fin
ok = FALSE
sel = getUpperKey()
when sel
// Equip player with weapon/armor
is 'E'
if (item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON) and !isEquipped(item)
doEquip(player, item)
return displayDone()
fin
break break
// Use an item // Use or Unequip an item
is 'U' is 'U'
if item->t_type == TYPE_ITEM if item->t_type == TYPE_PLAIN_ITEM or item->t_type == TYPE_FANCY_ITEM
return doUse(player, item) // general 'use' handled by outer engine, because it might involve graphics return doUse(player, item) // general 'use' handled by outer engine, because it might involve graphics
else elsif (item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON) and isEquipped(item)
beep doEquip(player, item)
displayItems(item, matchEquipped(player, item))
ok = TRUE
fin
break
// Trade an item
is 'T'
if global=>p_players=>p_nextObj and !isEquipped(item)
return doTrade(player, item)
fin
break
// Split a stack
is 'S'
if isSplittable(item)
if doSplit(player, item)
return NULL
fin
fin
break
// Join stacks
is 'J'
if isJoinable(item)
if doJoin(player, item)
return NULL
fin
fin fin
break break
// Destroy an item // Destroy an item
is 'D' is 'D'
if doDestroy(player, item); return NULL; fin if !isEquipped(item)
if doDestroy(player, item)
displayDone()
return NULL
fin
ok = TRUE
fin
break break
is $1B // Esc is $1B // Esc
return NULL return NULL
otherwise beep
wend wend
loop loop
return NULL return NULL
@ -538,18 +704,18 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
rawDisplayf1("^Y^I %s ^N\n", player=>s_name) rawDisplayf1("^Y^I %s ^N\n", player=>s_name)
redisplay = 1 redisplay = 1
totalItems = countList(player=>p_items) totalItems = countList(player=>p_items)
elsif redisplay > 0
clearInvRect()
fin
if redisplay > 0
if mode == 'I' if mode == 'I'
itemsOnPage = showInventory(player, i_page, 0)
showDerived(player) showDerived(player)
else // 'S' else // 'S'
showSkills(player) showSkills(player)
fin fin
redisplay = 0 elsif redisplay > 0
clearInvRect()
fin fin
if redisplay > 0 and mode == 'I'
itemsOnPage = showInventory(player, i_page, 0)
fin
redisplay = 0
if !noRepeatMenu if !noRepeatMenu
if mode == 'I' if mode == 'I'
@ -563,22 +729,24 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
// Get a key, do something // Get a key, do something
sel = getUpperKey() sel = getUpperKey()
when sel when sel
is '1' is '1'; is '2'; is '3'
is '2'
is '3'
sel = sel - '1' sel = sel - '1'
if player_num <> sel and countList(global=>p_players) > sel if countList(global=>p_players) > sel
player_num = sel player_num = sel
i_page = 0 i_page = 0
redisplay = 2 redisplay = 2
else else
beep beep
fin fin
break
// Next inventory page // Next inventory page
is '>' is '>'
if mode=='I' and totalItems > (i_page + 1) * INV_ROWS if mode=='I' and totalItems > (i_page + 1) * INV_ROWS
i_page++ i_page++
redisplay = 1 redisplay = 1
else
beep
noRepeatMenu = TRUE
fin fin
break break
// Previous inventory page // Previous inventory page
@ -587,6 +755,9 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
if mode=='I' and i_page if mode=='I' and i_page
i_page-- i_page--
redisplay = 1 redisplay = 1
else
beep
noRepeatMenu = TRUE
fin fin
break break
// Other operations... // Other operations...
@ -604,13 +775,16 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
break break
is '%' // add item cheat is '%' // add item cheat
if global->b_godmode if global->b_godmode
clearMainRect()
pGodModule=>godmode_addItem(player) pGodModule=>godmode_addItem(player)
redisplay = 1 redisplay = 2
fin fin
break break
is '9' // add player cheat is '9' // add player cheat
if global->b_godmode if global->b_godmode
clearMainRect()
pGodModule=>godmode_addPlayer() pGodModule=>godmode_addPlayer()
redisplay = 2
fin fin
break break
is '+' // level up cheat is '+' // level up cheat
@ -620,24 +794,30 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
fin fin
break break
is $1B // Esc is $1B // Esc
mmgr(CHECK_MEM, 0)
mmgr(HEAP_COLLECT, 0)
return NULL return NULL
otherwise otherwise
if sel == 'X' and mode <> 'I' if sel == 'X' and mode <> 'I' // switch from stats to inv
mode = 'I' mode = 'I'
redisplay = 2 redisplay = 2
elsif (sel == 'S' or sel == 'U') and mode <> 'S' elsif (sel == 'S' or sel == 'U') and mode <> 'S' // switch from inv to stats
mode = 'S' mode = 'S'
redisplay = 2 redisplay = 2
elsif mode == 'I' elsif mode == 'I'
sel = sel - 'A' sel = sel - 'A'
if sel >= 0 and sel < itemsOnPage if sel >= 0 and sel < itemsOnPage
item = interactWithItem(player, itemByNum(player, sel)) item = interactWithItem(player, itemByNum(player, i_page * INV_ROWS + sel))
if item; return item; fin // Use an item if item; return item; fin // Use an item
if countList(player=>p_items) <= i_page * INV_ROWS // destroyed last item on pg 2
i_page--
fin
redisplay = 2 redisplay = 2
else else
beep beep
noRepeatMenu = TRUE
fin fin
elsif mode == 'S' else // mode == 'S'
noRepeatMenu = TRUE noRepeatMenu = TRUE
if sel >= 'A' and (sel-'A' < nSkills) if sel >= 'A' and (sel-'A' < nSkills)
adjustSkill(player, sel - 'A', 1) adjustSkill(player, sel - 'A', 1)
@ -647,12 +827,10 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
beep beep
noRepeatMenu = FALSE noRepeatMenu = FALSE
fin fin
else
beep
fin fin
wend wend
until 0 until 0
return NULL return NULL // never reached
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -755,9 +933,9 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def formatEquipped(num)#1 def formatEquipped(num)#1
if num == 1 if num == 1
rawDisplayStr("Y") rawDisplayStr("yes")
else else
rawDisplayStr("N") rawDisplayStr("no")
fin fin
return 0 return 0
end end
@ -818,7 +996,6 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displayWeaponStats(pItem1, pItem2)#0 def displayWeaponStats(pItem1, pItem2)#0
displayTwoCol("Equip'd", pItem1, pItem2, b_flags, @equippedField, @formatEquipped) displayTwoCol("Equip'd", pItem1, pItem2, b_flags, @equippedField, @formatEquipped)
displayTwoCol("Uses", pItem1, pItem2, b_maxUses, @byteField, @formatNum)
displayTwoCol("Ammo", pItem1, pItem2, s_ammoKind, @wordField, @formatStr) displayTwoCol("Ammo", pItem1, pItem2, s_ammoKind, @wordField, @formatStr)
displayTwoCol("Clip", pItem1, pItem2, b_clipSize, @byteField, @formatNum) displayTwoCol("Clip", pItem1, pItem2, b_clipSize, @byteField, @formatNum)
displayTwoCol("Melee", pItem1, pItem2, r_meleeDmg, @wordField, @formatDice) displayTwoCol("Melee", pItem1, pItem2, r_meleeDmg, @wordField, @formatDice)
@ -832,25 +1009,19 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displayArmorStats(pItem1, pItem2)#0 def displayArmorStats(pItem1, pItem2)#0
displayTwoCol("Equip'd", pItem1, pItem2, b_flags, @equippedField, @formatEquipped) displayTwoCol("Equip'd", pItem1, pItem2, b_flags, @equippedField, @formatEquipped)
displayTwoCol("Uses", pItem1, pItem2, b_maxUses, @byteField, @formatNum)
displayTwoCol("Protec", pItem1, pItem2, b_armorValue, @byteField, @formatNum) displayTwoCol("Protec", pItem1, pItem2, b_armorValue, @byteField, @formatNum)
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayStuffStats(pItem1, pItem2)#0
// Nothing special
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def _displayItemStats(pItem1, pItem2)#1 def _displayItemStats(pItem1, pItem2)#1
word pMod1, pMod2 word pMod1, pMod2
// First, show the item type and name // First, show the item type and name
when pItem1->t_type when pItem1->t_type
is TYPE_ITEM; rawDisplayStr("\nItem"); break is TYPE_PLAIN_ITEM
is TYPE_FANCY_ITEM; rawDisplayStr("\nItem"); break
is TYPE_WEAPON; rawDisplayStr("\nWeapon"); break is TYPE_WEAPON; rawDisplayStr("\nWeapon"); break
is TYPE_ARMOR; rawDisplayStr("\nArmor"); break is TYPE_ARMOR; rawDisplayStr("\nArmor"); break
is TYPE_STUFF; rawDisplayStr("\nSupply"); break
otherwise fatal("tItem") otherwise fatal("tItem")
wend wend
tabTo(STATS_COL_1); _displayItemName(pItem1) tabTo(STATS_COL_1); _displayItemName(pItem1)
@ -862,25 +1033,28 @@ def _displayItemStats(pItem1, pItem2)#1
when pItem1->t_type when pItem1->t_type
is TYPE_WEAPON; displayWeaponStats(pItem1, pItem2); break is TYPE_WEAPON; displayWeaponStats(pItem1, pItem2); break
is TYPE_ARMOR; displayArmorStats(pItem1, pItem2); break is TYPE_ARMOR; displayArmorStats(pItem1, pItem2); break
is TYPE_STUFF; displayStuffStats(pItem1, pItem2); break
wend wend
// If either item has modifiers, show them // If either item has modifiers, show them
pMod1 = NULL pMod1 = NULL
if pItem1->t_type <> TYPE_STUFF; pMod1 = pItem1=>p_modifiers; fin if pItem1->t_type <> TYPE_PLAIN_ITEM; pMod1 = pItem1=>p_modifiers; fin
pMod2 = NULL pMod2 = NULL
if pItem2 if pItem2
if pItem2->t_type <> TYPE_STUFF; pMod2 = pItem2=>p_modifiers; fin if pItem2->t_type <> TYPE_PLAIN_ITEM; pMod2 = pItem2=>p_modifiers; fin
fin fin
if pMod1 or pMod2 if pMod1 or pMod2
rawDisplayStr("\nSpecial") rawDisplayStr("\nSpecial")
while pMod1 or pMod2 while pMod1 or pMod2
if pMod1 if pMod1
rawDisplayf3("^T%D%d %s", STATS_COL_1, pMod1=>w_modValue, pMod1=>s_name) rawDisplayf1("^T%D", STATS_COL_1)
if pMod1=>w_modValue >= 999; rawDisplayStr("Full "); else rawDisplayf1("%d ", pMod1=>w_modValue); fin
rawDisplayStr(pMod1=>s_name)
pMod1 = pMod1=>p_nextObj pMod1 = pMod1=>p_nextObj
fin fin
if pMod2 if pMod2
rawDisplayf3("^T%D%d %s", STATS_COL_2, pMod2=>w_modValue, pMod2=>s_name) rawDisplayf1("^T%D", STATS_COL_2)
if pMod2=>w_modValue >= 999; rawDisplayStr("Full "); else rawDisplayf1("%d ", pMod2=>w_modValue); fin
rawDisplayStr(pMod2=>s_name)
pMod2 = pMod2=>p_nextObj pMod2 = pMod2=>p_nextObj
fin fin
loop loop
@ -892,13 +1066,14 @@ end
// For non-countable items, display singular name. // For non-countable items, display singular name.
// For countable "stuff" (e.g. ammo), display the count and appropriate singular or plural name. // For countable "stuff" (e.g. ammo), display the count and appropriate singular or plural name.
def _displayItemName(pItem)#1 def _displayItemName(pItem)#1
if pItem->t_type == TYPE_STUFF isPlural = FALSE
setPlural(pItem=>w_count <> 1) if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
isPlural = TRUE
rawDisplayf1("%d ", pItem=>w_count) rawDisplayf1("%d ", pItem=>w_count)
else
setPlural(FALSE)
fin fin
rawDisplayf1("%s", pItem=>s_name) // use displayf to get proper plural processing buildString(@addToString)
printf1("%s", pItem=>s_name) // need proper plural processing
rawDisplayStr(finishString(isPlural))
return 0 return 0
end end

View File

@ -9,19 +9,17 @@
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Garbage collection pointer offsets within each type // Garbage collection pointer offsets within each type
byte typeTbl_Global[] = Global, p_players, p_benched, p_enemyGroups, p_combatFirst, p_encounterZones, s_mapName, p_gameFlags, 0 byte typeTbl_Global[] = Global, p_players, p_benched, p_enemyGroups, p_combatFirst, p_encounterZones, s_mapName, 0
byte typeTbl_Player[] = Player, p_nextObj, s_name, p_combatNext, p_skills, p_items, p_effects, 0 byte typeTbl_Player[] = Player, p_nextObj, s_name, p_combatNext, p_skills, p_items, 0
byte typeTbl_Modifier[] = Modifier, p_nextObj, s_name, 0 byte typeTbl_Modifier[] = Modifier, p_nextObj, s_name, 0
byte typeTbl_Effect[] = Effect, p_nextObj, s_name, 0 byte typeTbl_PlainItem[] = PlainItem, p_nextObj, s_name, 0
byte typeTbl_Item[] = Item, p_nextObj, s_name, s_itemKind, p_modifiers, 0 byte typeTbl_FancyItem[] = FancyItem, p_nextObj, s_name, s_itemKind, p_modifiers, 0
byte typeTbl_Weapon[] = Weapon, p_nextObj, s_name, s_itemKind, p_modifiers, s_ammoKind, s_combatText, 0 byte typeTbl_Weapon[] = Weapon, p_nextObj, s_name, s_itemKind, p_modifiers, s_ammoKind, s_combatText, 0
byte typeTbl_Armor[] = Armor, p_nextObj, s_name, s_itemKind, p_modifiers, 0 byte typeTbl_Armor[] = Armor, p_nextObj, s_name, s_itemKind, p_modifiers, 0
byte typeTbl_Stuff[] = Stuff, p_nextObj, s_name, s_itemKind, 0
byte typeTbl_Enemy[] = Enemy, p_nextObj, s_name, p_combatNext, s_attackText, 0 byte typeTbl_Enemy[] = Enemy, p_nextObj, s_name, p_combatNext, s_attackText, 0
byte typeTbl_EnemyGroup[] = EnemyGroup, p_nextObj, p_enemies, 0 byte typeTbl_EnemyGroup[] = EnemyGroup, p_nextObj, p_enemies, 0
byte typeTbl_EncounterZone[] = EncounterZone, p_nextObj, s_name, 0 byte typeTbl_EncounterZone[] = EncounterZone, p_nextObj, s_name, 0
word typeTbls = @typeTbl_Global, @typeTbl_Player, @typeTbl_Modifier, @typeTbl_Effect, @typeTbl_Item word typeTbls = @typeTbl_Global, @typeTbl_Player, @typeTbl_Modifier, @typeTbl_PlainItem, @typeTbl_FancyItem
word = @typeTbl_Weapon, @typeTbl_Armor, @typeTbl_Stuff, @typeTbl_Enemy, @typeTbl_EnemyGroup word = @typeTbl_Weapon, @typeTbl_Armor, @typeTbl_Enemy, @typeTbl_EnemyGroup, @typeTbl_EncounterZone
word = @typeTbl_EncounterZone
word = 0 word = 0

View File

@ -36,8 +36,7 @@ struc Global
word w_heapSize word w_heapSize
word w_typeHash word w_typeHash
// General flags maintained by scripts. Linked list of Modifiers. byte[32] ba_gameFlags // General flags maintained by scripts (array of 256 bits = 32 bytes)
word p_gameFlags
byte b_curAvatar byte b_curAvatar
// god mode flag // god mode flag
@ -86,7 +85,6 @@ struc Player
// Lists // Lists
word p_skills // list:Modifier word p_skills // list:Modifier
word p_items // list:Item word p_items // list:Item
word p_effects // list:Effect
end end
// Combat skills, weapon modifiers, etc. // Combat skills, weapon modifiers, etc.
@ -98,27 +96,29 @@ struc Modifier
word w_modValue word w_modValue
end end
// Buffs and debuffs, that last until a specified time // Plain items are for things that don't stack or have stat effects,
const TYPE_EFFECT = $83 // but may have meaning to scenario logic (e.g. keys, tokens, etc.)
struc Effect const TYPE_PLAIN_ITEM = $83
byte t_type struc PlainItem
word p_nextObj
byte s_name
word w_modValue
word w_endTurn
end
const TYPE_ITEM = $84
struc Item
byte t_type byte t_type
word p_nextObj word p_nextObj
word s_name word s_name
word s_itemKind
word w_price word w_price
word p_modifiers // list:modifier end
// Usables properties
byte b_maxUses const TYPE_FANCY_ITEM = $84
byte b_curUses struc FancyItem
// Plain item properties
byte t_type
word p_nextObj
word s_name
word w_price
// Fancy properties
word s_itemKind // for ammo
word p_modifiers // list:modifier, e.g. boost health, etc.
word w_count // zero for singular items, 1+ for countables
word w_storeAmount
word r_lootAmount
end end
const ITEM_FLAG_EQUIP = $80 // only one weapon/armor equipped (in use) at a time const ITEM_FLAG_EQUIP = $80 // only one weapon/armor equipped (in use) at a time
@ -127,19 +127,16 @@ const WEAPON_FLAG_SINGLE_USE = $01
const TYPE_WEAPON = $85 const TYPE_WEAPON = $85
struc Weapon struc Weapon
// Item properties // Plain item properties
byte t_type byte t_type
word p_nextObj word p_nextObj
word s_name word s_name
word s_itemKind
word w_price word w_price
word p_modifiers // list:modifier
// Usables properties
byte b_maxUses
byte b_curUses
// Weapon properties // Weapon properties
word s_itemKind // for skill matching
word p_modifiers // list:modifier
byte b_flags // WEAPON_FLAG_* above byte b_flags // WEAPON_FLAG_* above
word s_ammoKind word s_ammoKind // for matching to Stackable ammo
byte b_clipSize byte b_clipSize
byte b_clipCurrent byte b_clipCurrent
word r_meleeDmg // 3 hex digits: num dice, die size, add. E.g. $361 = 3d6+1 word r_meleeDmg // 3 hex digits: num dice, die size, add. E.g. $361 = 3d6+1
@ -151,37 +148,19 @@ end
const TYPE_ARMOR = $86 const TYPE_ARMOR = $86
struc Armor struc Armor
// General item properties // Plain item properties
byte t_type byte t_type
word p_nextObj word p_nextObj
word s_name word s_name
word s_itemKind
word w_price word w_price
word p_modifiers // list:modifier
// Usables properties
byte b_maxUses
byte b_curUses
// Armor properties // Armor properties
word s_itemKind
word p_modifiers // list:modifier
byte b_flags // ARMOR_FLAG_* above byte b_flags // ARMOR_FLAG_* above
byte b_armorValue byte b_armorValue
end end
// Countable things, e.g. ammo and pelts const TYPE_ENEMY = $87
const TYPE_STUFF = $87
struc Stuff
// General item properties
byte t_type
word p_nextObj
word s_name
word s_itemKind
word w_price
// Stuff properties
word w_count
word w_storeAmount
word r_lootAmount
end
const TYPE_ENEMY = $88
struc Enemy struc Enemy
byte t_type byte t_type
word p_nextObj word p_nextObj
@ -204,7 +183,7 @@ struc Enemy
word r_goldLoot // monetary loot when killed, as 3 hex digits for dice word r_goldLoot // monetary loot when killed, as 3 hex digits for dice
end end
const TYPE_ENEMY_GROUP = $89 const TYPE_ENEMY_GROUP = $88
struc EnemyGroup struc EnemyGroup
byte t_type byte t_type
word p_nextObj word p_nextObj
@ -212,7 +191,7 @@ struc EnemyGroup
byte b_enemyGroupRange byte b_enemyGroupRange
end end
const TYPE_ENCOUNTER_ZONE = $8A const TYPE_ENCOUNTER_ZONE = $89
struc EncounterZone struc EncounterZone
byte t_type byte t_type
word p_nextObj word p_nextObj

View File

@ -31,8 +31,8 @@ word pItemsModule, pPartyModule
const MAX_PAGE_ITEMS = 20 // should be plenty const MAX_PAGE_ITEMS = 20 // should be plenty
word pageItems[MAX_PAGE_ITEMS] word pageItems[MAX_PAGE_ITEMS]
word pagePrices[MAX_PAGE_ITEMS] word pagePrices[MAX_PAGE_ITEMS]
word pagePlayers[MAX_PAGE_ITEMS] byte playerNum, playerCount
word pMatchPlayer word pPlayer
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Definitions used by assembly code // Definitions used by assembly code
@ -61,14 +61,14 @@ def unloadExtraModules()#0
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displayBuyTitle(pageNum, nPages)#0 def displayTitle(titleAction, columnAction, pageNum, nPages)#0
clearWindow() clearWindow()
// Can't use centering mode on oversize window - font engine can't handle width > 255 rawDisplayf2("^I %s %s ", pPlayer=>s_name, titleAction)
rawDisplayStr("^Y^I Buying")
if (nPages > 1) if (nPages > 1)
rawDisplayf2(" - p. %d/%d", pageNum+1, nPages) rawDisplayf2("p.%d/%d ", pageNum+1, nPages)
fin fin
rawDisplayStr(" ^N\n^V014^LBrowse^T046Price^T085Item^L") rightJustifyStr(sprintf1(" %d party gold ", global=>w_gold), BIGWIN_RIGHT - 15)
rawDisplayf1("^N\n^V014^L%s^T046Price^T085Item^L", columnAction)
end end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -77,19 +77,45 @@ def displayItemLine(num)#0
pPartyModule()=>party_displayItemName(pageItems[num]) pPartyModule()=>party_displayItemName(pageItems[num])
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayPaging(action, nItems, pageNum, nPages)#0
rawDisplayStr("\n^V164")
if nItems or playerCount > 1
if nItems
rawDisplayf1("%s [A", action)
if nItems > 1; rawDisplayf1("-%c", nItems-1+'A'); fin
rawDisplayStr("], ")
fin
if nPages > 1
rawDisplayStr("Pg [")
if pageNum+1 < nPages; rawDisplayStr(">"); fin
if pageNum; rawDisplayStr("<"); fin
rawDisplayStr("], ")
fin
if playerCount > 1
rawDisplayf1("Plyr [1-%d], ", playerCount)
fin
rawDisplayStr("or [Esc].")
else
rawDisplayStr("Press [Esc].")
fin
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displayBuyPage(pItemTbl, markupRatio, pageNum, nPages)#1 def displayBuyPage(pItemTbl, markupRatio, pageNum, nPages)#1
byte itemNum byte itemNum
word pFunc, pItem word pFunc, pItem
displayBuyTitle(pageNum, nPages) // Clear stuff from previous page
mmgr(CHECK_MEM, 0)
mmgr(HEAP_COLLECT, 0) mmgr(HEAP_COLLECT, 0)
displayTitle("buying", "Browse", pageNum, nPages)
pFunc = pItemTbl + ((pageNum*PAGE_SIZE) << 1) pFunc = pItemTbl + ((pageNum*PAGE_SIZE) << 1)
for itemNum = 0 to PAGE_SIZE-1 for itemNum = 0 to PAGE_SIZE-1
if !(*pFunc); break; fin if !(*pFunc); break; fin
pItem = (*pFunc)() pItem = (*pFunc)()
if pItem->t_type == TYPE_STUFF if pItem->t_type == TYPE_FANCY_ITEM
pItem=>w_count = pItem=>w_storeAmount pItem=>w_count = pItem=>w_storeAmount
fin fin
pageItems[itemNum] = pItem pageItems[itemNum] = pItem
@ -97,14 +123,7 @@ def displayBuyPage(pItemTbl, markupRatio, pageNum, nPages)#1
displayItemLine(itemNum) displayItemLine(itemNum)
pFunc = pFunc + 2 pFunc = pFunc + 2
next next
displayPaging("Browse", itemNum, pageNum, nPages)
rawDisplayf1("\n^V166Gold: %d. Browse [A", global=>w_gold)
if itemNum > 1; rawDisplayf1("-%c", itemNum-1+'A'); fin
rawDisplayStr("], ")
if nPages > 1
rawDisplayf1("p. [1-%d], ", nPages)
fin
rawDisplayStr("or [Esc].")
return itemNum return itemNum
end end
@ -113,7 +132,7 @@ def displayItemBrowse(pItem1, price, pItem2)#0
clearWindow() clearWindow()
rawDisplayf1("^T108^I Browse ^N\n\n^T%D^LMerchandise^L", STATS_COL_1) rawDisplayf1("^T108^I Browse ^N\n\n^T%D^LMerchandise^L", STATS_COL_1)
if pItem2 if pItem2
rawDisplayf2("^T%D^L%s^L", STATS_COL_2, pMatchPlayer=>s_name) rawDisplayf2("^T%D^L%s^L", STATS_COL_2, pPlayer=>s_name)
fin fin
pPartyModule()=>party_displayItemStats(pItem1, pItem2) pPartyModule()=>party_displayItemStats(pItem1, pItem2)
rawDisplayf2("\n\nPrice^T%D%d", STATS_COL_1, price) rawDisplayf2("\n\nPrice^T%D%d", STATS_COL_1, price)
@ -122,17 +141,13 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def matchEquipped(pMatch, nSkip)#1 def matchEquipped(pMatch, nSkip)#1
word pPlayer, pItem word pItem
pPlayer = global=>p_players
pMatchPlayer = pPlayer
while pPlayer
pItem = pPlayer=>p_items pItem = pPlayer=>p_items
while pItem while pItem
if (pItem->t_type == pMatch->t_type) and (pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR) if (pItem->t_type == pMatch->t_type) and (pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR)
if (pItem->b_flags & ITEM_FLAG_EQUIP) if (pItem->b_flags & ITEM_FLAG_EQUIP)
if pItem->t_type <> TYPE_ARMOR or (pItem=>s_itemKind == pMatch=>s_itemKind) if pItem->t_type <> TYPE_ARMOR or (pItem=>s_itemKind == pMatch=>s_itemKind)
if nSkip == 0 if nSkip == 0
pMatchPlayer = pPlayer
return pItem return pItem
fin fin
nSkip = nSkip - 1 nSkip = nSkip - 1
@ -141,8 +156,6 @@ def matchEquipped(pMatch, nSkip)#1
fin fin
pItem = pItem=>p_nextObj pItem = pItem=>p_nextObj
loop loop
pPlayer = pPlayer=>p_nextObj
loop
return NULL return NULL
end end
@ -169,6 +182,12 @@ def askQuantity(nMax)#1
return num return num
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayMsg(msg, beep)#0
rawDisplayf1("^T000^C%s", msg)
pause(800)
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def browseItem(num)#0 def browseItem(num)#0
word pItem, price, compSkip, pComp, quantity word pItem, price, compSkip, pComp, quantity
@ -180,24 +199,22 @@ def browseItem(num)#0
displayItemBrowse(pItem, price, matchEquipped(pItem, compSkip)) displayItemBrowse(pItem, price, matchEquipped(pItem, compSkip))
displayItemMenu(price, compSkip or matchEquipped(pItem, 1+compSkip)) displayItemMenu(price, compSkip or matchEquipped(pItem, 1+compSkip))
sel = getUpperKey() sel = getUpperKey()
rawDisplayf1(" %c\n", sel)
if sel == 'B' and price <= global=>w_gold if sel == 'B' and price <= global=>w_gold
matchEquipped(pItem, compSkip) // to set pMatchPlayer pComp = scanForNamedObj(pPlayer=>p_items, pItem=>s_name)
pComp = scanForNamedObj(pMatchPlayer=>p_items, pItem=>s_name)
if pComp if pComp
if pItem->t_type == TYPE_STUFF if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 0
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count) pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
else else
rawDisplayStr("\nDuplicate item.") rawDisplayStr("^T000^CDuplicate item.")
beep() beep()
pause(1000) pause(400)
continue continue
fin fin
else else
addToList(@pMatchPlayer=>p_items, pItem) addToList(@pPlayer=>p_items, pItem)
fin fin
global=>w_gold = global=>w_gold - price global=>w_gold = global=>w_gold - price
rawDisplayStr("\nPurchase complete.") rawDisplayStr("^T000^CDone.")
pause(800) pause(800)
break break
elsif sel == 'N' and (compSkip or matchEquipped(pItem, 1+compSkip)) elsif sel == 'N' and (compSkip or matchEquipped(pItem, 1+compSkip))
@ -211,16 +228,24 @@ def browseItem(num)#0
loop loop
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def storeSetup()#0
loadExtraModules()
setBigWindow()
playerCount = countList(global=>p_players)
playerNum = 0
pPlayer = numToPlayer(playerNum)
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def _buyFromStore(storeCode, profitPercent)#1 def _buyFromStore(storeCode, profitPercent)#1
word pItemTbl, choice, ratio word pItemTbl, choice, ratio
byte nItemsOnPage, pageNum, nPages, redisplay byte nItemsOnPage, pageNum, nPages, redisplay
loadExtraModules() storeSetup()
pItemTbl = pItemsModule()=>items_forStoreCode(storeCode) pItemTbl = pItemsModule()=>items_forStoreCode(storeCode)
setBigWindow()
nPages = (countArray(pItemTbl) + PAGE_SIZE - 1) / PAGE_SIZE nPages = (countArray(pItemTbl) + PAGE_SIZE - 1) / PAGE_SIZE
pageNum = 0 pageNum = 0
@ -233,8 +258,13 @@ def _buyFromStore(storeCode, profitPercent)#1
fin fin
choice = getUpperKey() choice = getUpperKey()
redisplay = TRUE redisplay = TRUE
if choice >= '1' and (choice-'1') < nPages if choice == '<' and pageNum
pageNum = choice - '1' pageNum--
elsif choice == '>' and pageNum+1 < nPages
pageNum++
elsif choice >= '1' and (choice-'1') <= playerCount and (choice-'1') <> playerNum
playerNum = choice - '1'
pPlayer = numToPlayer(playerNum)
elsif choice >= 'A' and (choice-'A' < nItemsOnPage) elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
browseItem(choice-'A') browseItem(choice-'A')
elsif choice == $1B // Esc elsif choice == $1B // Esc
@ -246,35 +276,23 @@ def _buyFromStore(storeCode, profitPercent)#1
loop loop
unloadExtraModules() unloadExtraModules()
mmgr(CHECK_MEM, 0)
return mmgr(HEAP_COLLECT, 0) return mmgr(HEAP_COLLECT, 0)
end end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displaySellTitle(pageNum, nPages)#0
clearWindow()
// Can't use centering mode on oversize window - font engine can't handle width > 255
rawDisplayStr("^Y^I Selling")
if (nPages > 1)
rawDisplayf2(" - p. %d/%d", pageNum+1, nPages)
fin
rawDisplayStr(" ^N\n^V014^LSell^T046Amt.^T085Item^L")
end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def iterateSellables(skipItems, markdownRatio)#1 def iterateSellables(skipItems, markdownRatio)#1
word pPlayer, pItem, itemsOnPage, totalItems, price word pItem, itemsOnPage, totalItems, price
byte ok byte ok
itemsOnPage = 0 itemsOnPage = 0
totalItems = 0 totalItems = 0
pPlayer = global=>p_players
while pPlayer
pItem = pPlayer=>p_items pItem = pPlayer=>p_items
while pItem while pItem
ok = pItem=>w_price > 0 ok = pItem=>w_price > 0
if pItem->t_type == TYPE_STUFF if pItem->t_type == TYPE_FANCY_ITEM
ok = FALSE // too much trouble to figure out stuff prices ok = pItem=>w_count > 0 // too much trouble to figure out prices of stackables
elsif pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR elsif pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR
if pItem->b_flags & ITEM_FLAG_EQUIP; ok = FALSE; fin if pItem->b_flags & ITEM_FLAG_EQUIP; ok = FALSE; fin // don't sell equipped things
fin fin
if ok if ok
price = max(0, pItem=>w_price - addRatio(pItem=>w_price, markdownRatio)) price = max(0, pItem=>w_price - addRatio(pItem=>w_price, markdownRatio))
@ -284,7 +302,6 @@ def iterateSellables(skipItems, markdownRatio)#1
if totalItems >= skipItems and itemsOnPage < PAGE_SIZE if totalItems >= skipItems and itemsOnPage < PAGE_SIZE
pageItems[itemsOnPage] = pItem pageItems[itemsOnPage] = pItem
pagePrices[itemsOnPage] = price pagePrices[itemsOnPage] = price
pagePlayers[itemsOnPage] = pPlayer
displayItemLine(itemsOnPage) displayItemLine(itemsOnPage)
itemsOnPage++ itemsOnPage++
fin fin
@ -292,8 +309,6 @@ def iterateSellables(skipItems, markdownRatio)#1
fin fin
pItem = pItem=>p_nextObj pItem = pItem=>p_nextObj
loop loop
pPlayer = pPlayer=>p_nextObj
loop
if skipItems == 9999; return totalItems; fin if skipItems == 9999; return totalItems; fin
return itemsOnPage return itemsOnPage
end end
@ -301,18 +316,12 @@ end
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
def displaySellPage(markdownRatio, pageNum, nPages)#1 def displaySellPage(markdownRatio, pageNum, nPages)#1
word nItems word nItems
displaySellTitle(pageNum, nPages) displayTitle("selling", "Sell", pageNum, nPages)
nItems = iterateSellables(pageNum * PAGE_SIZE, markdownRatio) nItems = iterateSellables(pageNum * PAGE_SIZE, markdownRatio)
if !nItems if !nItems
rawDisplayStr("\n\nNothing to sell here.") rawDisplayStr("\n\nNothing to sell here.")
fin fin
rawDisplayf1("\n^V166Gold: %d. Sell [A", global=>w_gold) displayPaging("Sell", nItems, pageNum, nPages)
if nItems > 1; rawDisplayf1("-%c", nItems-1+'A'); fin
rawDisplayStr("], ")
if nPages > 1
rawDisplayf1("p. [1-%d], ", nPages)
fin
rawDisplayStr("or [Esc].")
return nItems return nItems
end end
@ -323,12 +332,10 @@ def sellItem(num)#0
pItem = pageItems[num] pItem = pageItems[num]
price = pagePrices[num] price = pagePrices[num]
clearWindow()
global=>w_gold = global=>w_gold + price global=>w_gold = global=>w_gold + price
removeFromList(@pagePlayers=>p_items, pItem) removeFromList(@pPlayer=>p_items, pItem)
rawDisplayStr("\nSale complete.") rawDisplayStr("^T000^CDone.")
pause(800) pause(800)
end end
@ -337,27 +344,34 @@ def _sellToStore(profitPercent)#1
word pItemTbl, choice, ratio word pItemTbl, choice, ratio
byte nItemsOnPage, pageNum, totalItems, nPages, redisplay byte nItemsOnPage, pageNum, totalItems, nPages, redisplay
loadExtraModules()
setBigWindow()
pageNum = 0 pageNum = 0
storeSetup()
ratio = percentToRatio(profitPercent) / 2 // half profit on buying, half on selling ratio = percentToRatio(profitPercent) / 2 // half profit on buying, half on selling
totalItems = iterateSellables(9999, 0) // initialize count for paging calcs
redisplay = TRUE redisplay = TRUE
while totalItems > 0 while TRUE
nPages = (totalItems + PAGE_SIZE - 1) / PAGE_SIZE // recalc each time since totalItems changes nPages = (totalItems + PAGE_SIZE - 1) / PAGE_SIZE // recalc each time since totalItems changes
pageNum = min(nPages-1, pageNum)
if redisplay if redisplay
nItemsOnPage = displaySellPage(ratio, pageNum, nPages) nItemsOnPage = displaySellPage(ratio, pageNum, nPages)
fin fin
choice = getUpperKey() choice = getUpperKey()
redisplay = TRUE redisplay = TRUE
if choice >= '1' and (choice-'1') < nPages if choice == '<' and pageNum
pageNum = choice - '1' pageNum--
elsif choice == '>' and pageNum+1 < nPages
pageNum++
elsif choice >= '1' and (choice-'1') <= playerCount
playerNum = choice - '1'
pPlayer = numToPlayer(playerNum)
totalItems = iterateSellables(9999, 0)
elsif choice >= 'A' and (choice-'A' < nItemsOnPage) elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
sellItem(choice-'A') sellItem(choice-'A')
totalItems = iterateSellables(9999, 0) totalItems = iterateSellables(9999, 0)
if totalItems <= pageNum * PAGE_SIZE // sold last item on pg 2
pageNum--
fin
elsif choice == $1B // Esc elsif choice == $1B // Esc
break break
else else
@ -367,6 +381,7 @@ def _sellToStore(profitPercent)#1
loop loop
unloadExtraModules() unloadExtraModules()
mmgr(CHECK_MEM, 0)
return mmgr(HEAP_COLLECT, 0) return mmgr(HEAP_COLLECT, 0)
end end

View File

@ -1763,6 +1763,8 @@ pl_advance: !zone
pha pha
adc walkDirs+1,x adc walkDirs+1,x
sta playerX+1 sta playerX+1
jsr .chk
sta .ora+1
lda playerY lda playerY
pha pha
@ -1773,27 +1775,15 @@ pl_advance: !zone
pha pha
adc walkDirs+3,x adc walkDirs+3,x
sta playerY+1 sta playerY+1
jsr .chk
; Check if the new position is blocked .ora ora #11 ; self-modified above
jsr calcMapOrigin
ldy playerX+1
lda (pMap),y
and #$1F
beq .ok ; empty tiles are never blocked
tax
jsr getTileFlags
sta tmp+1
and #2 ; tile flag 2 is for obstructions
beq .ok beq .ok
; Blocked! Restore old position. ; Blocked! Restore old position.
pla ldx #3
sta playerY+1 - pla
pla sta playerX,x
sta playerY dex
pla bpl -
sta playerX+1
pla
sta playerX
ldy #0 ldy #0
beq .done beq .done
.ok ; Not blocked. See if we're in a new map tile. .ok ; Not blocked. See if we're in a new map tile.
@ -1820,6 +1810,20 @@ pl_advance: !zone
.done tya ; retrieve ret value .done tya ; retrieve ret value
ldy #0 ; hi byte of ret is always 0 ldy #0 ; hi byte of ret is always 0
rts ; all done rts ; all done
; Check if the new position is blocked
.chk stx .rstx+1
jsr calcMapOrigin
ldy playerX+1
lda (pMap),y
and #$1F
beq .rstx ; empty tiles are never blocked
tax
jsr getTileFlags
sta tmp+1
and #2 ; tile flag 2 is for obstructions
.rstx ldx #11 ; self-modified above
cmp #0
rts
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Swap tiles at two positions. ; Swap tiles at two positions.