mirror of
https://github.com/badvision/lawless-legends.git
synced 2025-02-22 03:29:01 +00:00
Merge branch 'master' into newplasma
This commit is contained in:
commit
5286d766df
@ -19,11 +19,13 @@ import java.nio.ByteBuffer
|
||||
import java.nio.channels.Channels
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.util.Calendar
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.LinkedHashMap
|
||||
import java.security.MessageDigest
|
||||
import javax.xml.bind.DatatypeConverter
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.util.Node
|
||||
|
||||
/**
|
||||
*
|
||||
@ -81,6 +83,7 @@ class A2PackPartitions
|
||||
def modules = [:] // module name to module.num, module.buf
|
||||
def bytecodes = [:] // module name to bytecode.num, bytecode.buf
|
||||
def fixups = [:] // module name to fixup.num, fixup.buf
|
||||
def gameFlags = [:] // flag name to number
|
||||
|
||||
def itemNameToFunc = [:]
|
||||
def playerNameToFunc = [:]
|
||||
@ -773,6 +776,12 @@ class A2PackPartitions
|
||||
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)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
@ -1566,6 +1599,12 @@ class A2PackPartitions
|
||||
{
|
||||
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())
|
||||
maps2D.each { k, v ->
|
||||
tmp.put((byte) ((parseOrder(v.order) < 0) ? 255 : v.buf.partNum))
|
||||
@ -1584,6 +1623,8 @@ class A2PackPartitions
|
||||
name:"resourceIndex", buf:code["resourceIndex"].buf]
|
||||
part.chunks[["code", "resourceIndex"]] = chunk
|
||||
part.spaceUsed += calcChunkLen(chunk)
|
||||
|
||||
return combinedVersion
|
||||
}
|
||||
|
||||
def fillAllDisks()
|
||||
@ -1637,7 +1678,7 @@ class A2PackPartitions
|
||||
assert allMaps.isEmpty : "All data must fit within $MAX_DISKS disks."
|
||||
|
||||
// Add the special resource index to disk 1
|
||||
addResourceIndex(partChunks[0])
|
||||
def gameVersion = addResourceIndex(partChunks[0])
|
||||
|
||||
// And write out each disk
|
||||
partChunks.each { part ->
|
||||
@ -1649,6 +1690,8 @@ class A2PackPartitions
|
||||
def spaceUsed = part.spaceUsed // use var to avoid gigantic assert fail msg
|
||||
assert spaceUsed == partFile.length()
|
||||
}
|
||||
|
||||
println "Game version: V $gameVersion"
|
||||
}
|
||||
|
||||
def writePartition(stream, partNum, chunks)
|
||||
@ -1859,6 +1902,7 @@ class A2PackPartitions
|
||||
def uncompData = readBinary(inDir + "build/" + codeName + ".b")
|
||||
|
||||
addToCache("code", code, codeName, hash, compress(uncompData))
|
||||
updateEngineStamp(codeName, hash)
|
||||
}
|
||||
|
||||
def assembleCore(inDir)
|
||||
@ -1878,8 +1922,10 @@ class A2PackPartitions
|
||||
def file = jitCopy(
|
||||
new File("build/tools/${name=="PRORWTS" ? "ProRWTS/PRORWTS2" : "PLASMA/src/PLVM02"}#4000"))
|
||||
hash = file.lastModified()
|
||||
if (!grabFromCache("sysCode", sysCode, name, hash))
|
||||
if (!grabFromCache("sysCode", sysCode, name, hash)) {
|
||||
addToCache("sysCode", sysCode, name, hash, compress(readBinary(file.toString())))
|
||||
updateEngineStamp(name, hash)
|
||||
}
|
||||
}
|
||||
else {
|
||||
hash = getLastDep(new File(inDir, "${name}.s"))
|
||||
@ -1892,6 +1938,7 @@ class A2PackPartitions
|
||||
addToCache("sysCode", sysCode, name, hash,
|
||||
(name ==~ /loader|decomp/) ? [data:uncompData, len:uncompData.length, compressed:false]
|
||||
: compress(uncompData))
|
||||
updateEngineStamp(name, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1952,6 +1999,8 @@ class A2PackPartitions
|
||||
addToCache("modules", modules, moduleName, hash, module)
|
||||
addToCache("bytecodes", bytecodes, moduleName, hash, bytecode)
|
||||
addToCache("fixups", fixups, moduleName, hash, fixup)
|
||||
if (!(moduleName ==~ /.*(gs|gen)_.*/ || codeDir ==~ /.*mapScript.*/))
|
||||
updateEngineStamp(moduleName, hash)
|
||||
}
|
||||
|
||||
def readAllCode()
|
||||
@ -1980,6 +2029,7 @@ class A2PackPartitions
|
||||
compileModule("gen_enemies", "src/plasma/")
|
||||
compileModule("gen_items", "src/plasma/")
|
||||
compileModule("gen_players", "src/plasma/")
|
||||
compileModule("gen_flags", "src/plasma/")
|
||||
globalScripts.each { name, nArgs ->
|
||||
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()
|
||||
{
|
||||
File cacheFile = new File("build/world.cache")
|
||||
@ -2127,11 +2211,14 @@ class A2PackPartitions
|
||||
addResourceDep("map", curMapName, toType, toName)
|
||||
}
|
||||
|
||||
def pack(xmlPath, dataIn)
|
||||
def pack(xmlFile, dataIn)
|
||||
{
|
||||
// Save time by using cache of previous run
|
||||
readCache()
|
||||
|
||||
// Record scenario timestamp
|
||||
cache["scenarioStamp"] = [hash: xmlFile.lastModified()]
|
||||
|
||||
// Record global script names
|
||||
recordGlobalScripts(dataIn)
|
||||
|
||||
@ -2242,6 +2329,9 @@ class A2PackPartitions
|
||||
// Number all the maps and record them with names
|
||||
numberMaps(dataIn)
|
||||
|
||||
// Assign a number to each game flag
|
||||
numberGameFlags(dataIn)
|
||||
|
||||
// Form the translation from item name to function name (and ditto
|
||||
// for players)
|
||||
allItemFuncs(dataIn.global.sheets.sheet)
|
||||
@ -2598,6 +2688,67 @@ end
|
||||
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)
|
||||
{
|
||||
out.println(
|
||||
@ -2629,26 +2780,21 @@ end
|
||||
"${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)
|
||||
{
|
||||
out.println(
|
||||
" return makeItem(" +
|
||||
"${escapeString(parseStringAttr(row, "name"))}, " +
|
||||
"${parseWordAttr(row, "price")}, " +
|
||||
"${parseModifier(row, "bonus-value", "bonus-attribute")}, " +
|
||||
"${parseByteAttr(row, "number-of-uses")})")
|
||||
def name = parseStringAttr(row, "name")
|
||||
def price = parseWordAttr(row, "price")
|
||||
def modifier = parseModifier(row, "bonus-value", "bonus-attribute")
|
||||
def kind = parseStringAttr(row, "ammo-kind")
|
||||
def count = parseWordAttr(row, "count")
|
||||
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)
|
||||
@ -2688,7 +2834,7 @@ end
|
||||
def itemFunc = itemNameToFunc[name]
|
||||
assert itemFunc : "Can't locate item '$name'"
|
||||
if (num > 1)
|
||||
out.println(" addToList(@p=>p_items, setStuffCount(itemScripts()=>$itemFunc(), $num))")
|
||||
out.println(" addToList(@p=>p_items, setItemCount(itemScripts()=>$itemFunc(), $num))")
|
||||
else
|
||||
out.println(" addToList(@p=>p_items, itemScripts()=>$itemFunc())")
|
||||
}
|
||||
@ -2849,24 +2995,22 @@ def makeWeapon_pt2(p, attack0, attack1, attack2, weaponRange, combatText, single
|
||||
return p
|
||||
end
|
||||
|
||||
def makeStuff(name, kind, price, count, storeAmount, lootAmount)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_STUFF)
|
||||
def makePlainItem(name, price)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_PLAIN_ITEM)
|
||||
p=>s_name = mmgr(HEAP_INTERN, name)
|
||||
p=>s_itemKind = mmgr(HEAP_INTERN, kind)
|
||||
p=>w_price = price
|
||||
p=>w_count = count
|
||||
p=>w_storeAmount = storeAmount
|
||||
p=>r_lootAmount = lootAmount
|
||||
return p
|
||||
end
|
||||
|
||||
def makeItem(name, price, modifier, maxUses)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_ITEM)
|
||||
def makeFancyItem(name, price, kind, modifiers, count, storeAmount, lootAmount)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_FANCY_ITEM)
|
||||
p=>s_name = mmgr(HEAP_INTERN, name)
|
||||
p=>w_price = price
|
||||
p=>p_modifiers = modifier
|
||||
p->b_maxUses = maxUses
|
||||
p->b_curUses = 0
|
||||
p=>s_itemKind = mmgr(HEAP_INTERN, kind)
|
||||
p=>p_modifiers = modifiers
|
||||
p=>w_count = count
|
||||
p=>w_storeAmount = storeAmount
|
||||
p=>r_lootAmount = lootAmount
|
||||
return p
|
||||
end
|
||||
|
||||
@ -2880,7 +3024,7 @@ end
|
||||
switch (typeName) {
|
||||
case "weapon": genWeapon(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
|
||||
default: assert false
|
||||
}
|
||||
@ -2993,11 +3137,11 @@ def makePlayer_pt2(p, health, level, aiming, handToHand, dodging, gender)#1
|
||||
return p
|
||||
end
|
||||
|
||||
def setStuffCount(p, ct)#1
|
||||
if p->t_type == TYPE_STUFF
|
||||
def setItemCount(p, ct)#1
|
||||
if p->t_type == TYPE_FANCY_ITEM
|
||||
p->w_count = ct
|
||||
else
|
||||
fatal(\"stuffct\")
|
||||
fatal(\"itemct\")
|
||||
fin
|
||||
return p // for chaining
|
||||
end
|
||||
@ -3088,7 +3232,7 @@ end
|
||||
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
|
||||
// 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.
|
||||
numberMaps(dataIn)
|
||||
|
||||
// Assign a number to each game flag
|
||||
numberGameFlags(dataIn)
|
||||
|
||||
// Form the translation from item name to function name (and ditto
|
||||
// for players)
|
||||
allItemFuncs(dataIn.global.sheets.sheet)
|
||||
@ -3152,6 +3299,9 @@ end
|
||||
genAllGlobalScripts(dataIn.global.scripts.script)
|
||||
curMapName = null
|
||||
|
||||
// Generate a mapping of flags, for debugging purposes.
|
||||
genAllFlags()
|
||||
|
||||
// Translate enemies, weapons, etc. to code
|
||||
genAllItems(dataIn.global.sheets.sheet)
|
||||
genAllEnemies(dataIn.global.sheets.sheet.find { it?.@name.equalsIgnoreCase("enemies") })
|
||||
@ -3206,7 +3356,7 @@ end
|
||||
|
||||
def createHddImage()
|
||||
{
|
||||
println "Creating hdd image."
|
||||
//println "Creating hdd image."
|
||||
|
||||
// Copy the combined core executable to the output directory
|
||||
copyIfNewer(new File("build/src/core/build/LEGENDOS.SYSTEM.sys#2000"),
|
||||
@ -3231,7 +3381,7 @@ end
|
||||
|
||||
def createFloppyImages()
|
||||
{
|
||||
println "Creating floppy images."
|
||||
//println "Creating floppy images."
|
||||
|
||||
// We'll be copying stuff from the hdd directory
|
||||
def hddDir = new File("build/root")
|
||||
@ -3442,6 +3592,7 @@ end
|
||||
out << "include \"../plasma/gamelib.plh\"\n"
|
||||
out << "include \"../plasma/globalDefs.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_items.plh\"\n"
|
||||
out << "include \"../plasma/gen_modules.plh\"\n"
|
||||
@ -3929,14 +4080,16 @@ end
|
||||
|
||||
def packGetFlag(blk)
|
||||
{
|
||||
def name = getSingle(blk.field, 'NAME').text()
|
||||
out << "getGameFlag(${escapeString(name)})"
|
||||
def name = getSingle(blk.field, 'NAME').text().trim().toLowerCase()
|
||||
assert gameFlags.containsKey(name)
|
||||
out << "getGameFlag(GF_${humanNameToSymbol(name, true)})"
|
||||
}
|
||||
|
||||
def packChangeFlag(blk)
|
||||
{
|
||||
def name = getSingle(blk.field, 'NAME').text()
|
||||
outIndented("setGameFlag(${escapeString(name)}, ${blk.@type == 'interaction_set_flag' ? 1 : 0})\n")
|
||||
def name = getSingle(blk.field, 'NAME').text().trim().toLowerCase()
|
||||
assert gameFlags.containsKey(name)
|
||||
outIndented("setGameFlag(GF_${humanNameToSymbol(name, true)}, ${blk.@type == 'interaction_set_flag' ? 1 : 0})\n")
|
||||
}
|
||||
|
||||
def isStringExpr(blk)
|
||||
@ -4009,6 +4162,10 @@ end
|
||||
}
|
||||
}
|
||||
|
||||
def packLogicNegate(blk) {
|
||||
out << "!("; packExpr(getSingle(blk.value, "BOOL").block[0]); out << ")"
|
||||
}
|
||||
|
||||
def packMathArithmetic(blk)
|
||||
{
|
||||
def op = getSingle(blk.field, "OP").text()
|
||||
@ -4078,6 +4235,9 @@ end
|
||||
case 'logic_operation':
|
||||
packLogicOperation(blk)
|
||||
break
|
||||
case 'logic_negate':
|
||||
packLogicNegate(blk)
|
||||
break
|
||||
case 'variables_get':
|
||||
packVarGet(blk)
|
||||
break
|
||||
|
@ -747,15 +747,8 @@ gcHash_chk: !zone
|
||||
rts
|
||||
.corrup jmp heapCorrupt
|
||||
|
||||
!if DEBUG = 0 {
|
||||
debugOnly:
|
||||
jsr inlineFatal : !text "DebugOnly",0
|
||||
}
|
||||
|
||||
; Verify integrity of memory manager structures
|
||||
memCheck: !zone
|
||||
!if DEBUG = 0 { jmp debugOnly }
|
||||
!if DEBUG {
|
||||
jsr heapCheck ; heap check (if there is one)
|
||||
ldx #0 ; check main bank
|
||||
jsr .chk
|
||||
@ -811,7 +804,6 @@ heapCheck: !zone
|
||||
cmp heapEndPg ; or >= than end of heap
|
||||
bcc .tscan
|
||||
; fall through to heapCorrupt...
|
||||
} ; if DEBUG
|
||||
|
||||
heapCorrupt:
|
||||
+prWord pTmp
|
||||
@ -1333,16 +1325,10 @@ aux_dispatch:
|
||||
!if SANITY_CHECK {
|
||||
saneStart: !zone {
|
||||
sta saneEnd+2 ; save cmd num for end-checking
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
cmp #ADVANCE_ANIMS
|
||||
beq .skip
|
||||
pha
|
||||
jsr saneCheck
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
tay
|
||||
+prChr 'M'
|
||||
lda isAuxCmd
|
||||
beq +
|
||||
@ -1359,7 +1345,7 @@ saneStart: !zone {
|
||||
beq +
|
||||
+prY
|
||||
+ pla
|
||||
rts
|
||||
.skip rts
|
||||
}
|
||||
|
||||
saneCheck: !zone {
|
||||
@ -1373,6 +1359,8 @@ saneCheck: !zone {
|
||||
saneEnd: !zone {
|
||||
pha
|
||||
lda #$11 ; self-modified earlier by saneStart
|
||||
cmp #ADVANCE_ANIMS
|
||||
beq .skip
|
||||
cmp #REQUEST_MEMORY
|
||||
beq .val
|
||||
cmp #QUEUE_LOAD
|
||||
@ -1385,17 +1373,9 @@ saneEnd: !zone {
|
||||
bne .noval
|
||||
.val +prStr : !text "->",0
|
||||
+prYX
|
||||
.noval tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
jsr saneCheck
|
||||
.noval jsr saneCheck
|
||||
+prStr : !text "m.",0
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
.skip pla
|
||||
rts
|
||||
}
|
||||
}
|
||||
@ -2787,6 +2767,7 @@ advSingleAnim:
|
||||
rts
|
||||
.dbgout +crout
|
||||
+waitKey
|
||||
bit $c050
|
||||
sta setAuxRd
|
||||
sta setAuxWr
|
||||
rts
|
||||
@ -2828,12 +2809,14 @@ applyPatch:
|
||||
; loop to skip patches until we find the right one
|
||||
- dec reqLen ; it starts at 1, which means first patch.
|
||||
beq +
|
||||
ldy #1
|
||||
ldy #0
|
||||
lda (pSrc),y ; low byte of patch len
|
||||
pha
|
||||
iny
|
||||
lda (pSrc),y ; hi byte of patch len
|
||||
inx ; -> pSrc+1
|
||||
jsr .ptradd ; skip by # pages in patch
|
||||
dey
|
||||
lda (pSrc),y ; low byte of patch len
|
||||
pla ; get lo byte of len back
|
||||
jsr .srcadd ; skip pSrc past last partial page in patch
|
||||
jmp -
|
||||
+ !if DEBUG = 2 { jsr .dbgC2 }
|
||||
|
@ -132,7 +132,7 @@ def rollPlayerHit(pPlayer, pWeapon, pEnemy, sAction)
|
||||
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 roll >= chance
|
||||
setPlural(0)
|
||||
isPlural = FALSE
|
||||
displayf3("\n%s %s at %s but misses.\n", pPlayer=>s_name, sAction, pEnemy=>s_name)
|
||||
return FALSE
|
||||
fin
|
||||
@ -143,7 +143,7 @@ end
|
||||
def rollEnemyDodge(pPlayer, pEnemy, sAction)
|
||||
// Enemy chance to dodge is taken from their chance to hit divided by 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)
|
||||
displayf1("but %s dodges.\n", pEnemy=>s_name)
|
||||
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)
|
||||
getUpperKey
|
||||
fin
|
||||
setPlural(0)
|
||||
isPlural = FALSE
|
||||
buildString(@addToString)
|
||||
printf3("\n%s %s %s ", pPlayer=>s_name, sAction, pEnemy=>s_name)
|
||||
printf1("for %d damage.", dmg)
|
||||
@ -290,7 +290,7 @@ def reload(pl, pWeapon, echo)#0
|
||||
word item
|
||||
byte orig, n
|
||||
|
||||
setPlural(FALSE)
|
||||
isPlural = FALSE
|
||||
|
||||
// If ammo type is null, it means weapon doesn't use ammo in traditional sense.
|
||||
if !pWeapon=>s_ammoKind
|
||||
@ -301,7 +301,7 @@ def reload(pl, pWeapon, echo)#0
|
||||
// Find matching ammo
|
||||
item = pl=>p_items
|
||||
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
|
||||
fin
|
||||
item = item=>p_nextObj
|
||||
@ -366,7 +366,7 @@ def displayOpponents()#0
|
||||
fin
|
||||
first = FALSE
|
||||
count = countListFiltered(p=>p_enemies, p_nextObj, @canFight)
|
||||
setPlural(count <> 1)
|
||||
isPlural = count <> 1
|
||||
if (p=>p_enemies=>r_groupSize == 0)
|
||||
displayf2("%s at %d'", p=>p_enemies=>s_name, p->b_enemyGroupRange)
|
||||
else
|
||||
@ -678,7 +678,7 @@ def enemyCombatTurn(pe)#1
|
||||
pl = randomFromListFiltered(global=>p_players, p_nextObj, @canFight)
|
||||
if !pl; return FALSE; fin
|
||||
|
||||
setPlural(FALSE)
|
||||
isPlural = FALSE
|
||||
displayf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name)
|
||||
|
||||
// Roll to hit
|
||||
@ -851,7 +851,7 @@ def addItem(pl, pItem)#1
|
||||
word pComp
|
||||
pComp = scanForNamedObj(pl=>p_items, pItem=>s_name)
|
||||
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)
|
||||
return TRUE
|
||||
else
|
||||
@ -882,16 +882,17 @@ def collectLootAndXP()#2
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
itemFunc = randomFromArray(pItemsModule()=>items_forLootCode(enemy=>s_lootCode))
|
||||
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)
|
||||
fin
|
||||
if addItem(global=>p_players, pItem)
|
||||
if pItem->t_type == TYPE_STUFF and pItem=>w_count > 1
|
||||
setPlural(TRUE)
|
||||
displayf2("You find %d %s! ", pItem=>w_count, pItem=>s_name)
|
||||
displayStr("You find ")
|
||||
if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
|
||||
isPlural = TRUE
|
||||
displayf2("%d %s! ", pItem=>w_count, pItem=>s_name)
|
||||
else
|
||||
setPlural(FALSE)
|
||||
displayf2("You find %s%s! ", anOrA(pItem=>s_name), pItem=>s_name)
|
||||
isPlural = FALSE
|
||||
displayf2("%s%s! ", anOrA(pItem=>s_name), pItem=>s_name)
|
||||
fin
|
||||
fin
|
||||
lootedItem = TRUE
|
||||
@ -908,6 +909,11 @@ end
|
||||
def startCombat(mapCode)#1
|
||||
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
|
||||
isFleeing = FALSE
|
||||
combatDebug = FALSE
|
||||
@ -915,6 +921,7 @@ def startCombat(mapCode)#1
|
||||
|
||||
// Display portrait of first group
|
||||
setPortrait(global=>p_enemyGroups=>p_enemies->b_image)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
|
||||
// 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.
|
||||
@ -932,7 +939,7 @@ def startCombat(mapCode)#1
|
||||
p = global=>p_enemyGroups
|
||||
while p
|
||||
n = countList(p=>p_enemies)
|
||||
setPlural(n <> 1)
|
||||
isPlural = n <> 1
|
||||
s = callGlobalFunc(GS_ENEMY_INTRO, 0, 0, 0)
|
||||
displayf2(s, n, p=>p_enemies=>s_name)
|
||||
p = p=>p_nextObj
|
||||
|
@ -37,6 +37,7 @@ predef _newOrLoadGame(ask)#1
|
||||
word[] funcTbl = @_saveGame, @_loadGame, @_newOrLoadGame
|
||||
|
||||
byte[] game1_filename = "GAME.1.SAVE"
|
||||
byte[] legendos_filename = "LEGENDOS.SYSTEM"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Definitions used by assembly code
|
||||
@ -223,15 +224,23 @@ def _rwGame(cmd)#0
|
||||
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
|
||||
global=>w_heapSize = mmgr(HEAP_COLLECT, 0) - HEAP_BOTTOM
|
||||
|
||||
// Copy data to main memory, and write it out.
|
||||
showMapName("Saving game...")
|
||||
memcpy(HEAP_BOTTOM, LOAD_SAVE_BUF, HEAP_SIZE) // LC to low mem
|
||||
_rwGame(RWTS_WRITE)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def _saveGame()#1
|
||||
showMapName("Saving game...")
|
||||
saveInternal()
|
||||
return 0
|
||||
end
|
||||
|
||||
@ -246,12 +255,12 @@ def loadInternal()#1
|
||||
p_loaded = LOAD_SAVE_BUF
|
||||
if p_loaded=>w_heapSize == 0
|
||||
return FALSE // no game saved yet
|
||||
fin
|
||||
if p_loaded=>w_heapSize < 100 or p_loaded=>w_heapSize > HEAP_SIZE
|
||||
fatal("Corrupt game file.")
|
||||
elsif p_loaded=>w_heapSize < 100 or p_loaded=>w_heapSize > HEAP_SIZE or p_loaded=>w_typeHash <> typeHash
|
||||
fatal("Incompatible game file.")
|
||||
fin
|
||||
memcpy(LOAD_SAVE_BUF, HEAP_BOTTOM, HEAP_SIZE) // low mem to LC
|
||||
initHeap(p_loaded=>w_heapSize)
|
||||
mmgr(CHECK_MEM, 0) // make sure heap is valid
|
||||
return TRUE
|
||||
end
|
||||
|
||||
@ -270,21 +279,123 @@ def _loadGame()#1
|
||||
return 0
|
||||
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
|
||||
word cursX, cursY
|
||||
displayStr("\nCharacter name?\n")
|
||||
cursX, cursY = getCursor()
|
||||
setWindow(cursY+24, cursY+24+18, cursX+154, cursX+154+62)
|
||||
clearWindow()
|
||||
global=>p_players=>s_name = getStringResponse()
|
||||
setWindow2()
|
||||
setCursor(cursX, cursY)
|
||||
while TRUE
|
||||
displayStr("Character name?\n")
|
||||
cursX, cursY = getCursor()
|
||||
setWindow(cursY+24, cursY+24+18, cursX+154, cursX+154+62)
|
||||
clearWindow()
|
||||
global=>p_players=>s_name = getStringResponse()
|
||||
setWindow2()
|
||||
setCursor(cursX, cursY)
|
||||
if isValidName(global=>p_players=>s_name); break; fin
|
||||
displayStr("\nInvalid name.\n\n")
|
||||
beep()
|
||||
loop
|
||||
end
|
||||
|
||||
def getCharacterGender()#0
|
||||
displayStr("\n\nGender? (M/F/N/...) \n")
|
||||
global=>p_players->c_gender = getUpperKey()
|
||||
displayChar(global=>p_players->c_gender)
|
||||
byte gender
|
||||
repeat
|
||||
displayStr("\n\nGender? (M/F/N/...) \n")
|
||||
gender = getUpperKey()
|
||||
displayChar(gender)
|
||||
until gender >= 'A' and gender <= 'Z'
|
||||
global=>p_players->c_gender = gender
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -303,6 +414,7 @@ def newGame()#0
|
||||
showMapName("New game")
|
||||
setWindow2()
|
||||
newGameModule()()
|
||||
clearWindow()
|
||||
getCharacterName()
|
||||
getCharacterGender()
|
||||
if global=>p_players->b_skillPoints
|
||||
@ -310,52 +422,30 @@ def newGame()#0
|
||||
fin
|
||||
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
|
||||
byte key
|
||||
|
||||
if !gameExists()
|
||||
newGame(); return 1
|
||||
elsif !ask
|
||||
loadInternal(); return 0
|
||||
fin
|
||||
|
||||
if !ask
|
||||
loadInternal()
|
||||
return 0
|
||||
fin
|
||||
|
||||
textHome()
|
||||
^$c053
|
||||
^$25 = 20
|
||||
puts("\n N)ew game, or L)oad last game? ")
|
||||
|
||||
while TRUE
|
||||
key = getUpperKey()
|
||||
if key == 'N'
|
||||
^$c052
|
||||
newGame()
|
||||
return 1
|
||||
elsif key == 'L'
|
||||
^$c052
|
||||
if loadInternal()
|
||||
return 0
|
||||
fin
|
||||
fin
|
||||
textHome()
|
||||
^$25 = 20
|
||||
puts("\n Game: N)ew, L)oad, I)mport?")
|
||||
key = getTextKey()
|
||||
when key
|
||||
is 'N'
|
||||
newGame(); return 1
|
||||
is 'L'
|
||||
if loadInternal(); return 0; fin
|
||||
break
|
||||
is 'I'
|
||||
if importGame(); return 0; fin
|
||||
wend
|
||||
beep()
|
||||
loop
|
||||
return 0
|
||||
|
@ -39,6 +39,7 @@ import gamelib
|
||||
predef countArray(arr)#1
|
||||
predef countList(p)#1
|
||||
predef countListFiltered(p, offset, filterFunc)#1
|
||||
predef crout()#0
|
||||
predef displayChar(chr)#0
|
||||
predef displayf1(fmt, arg1)#0
|
||||
predef displayf2(fmt, arg1, arg2)#0
|
||||
@ -46,7 +47,6 @@ import gamelib
|
||||
predef displayStr(str)#0
|
||||
predef encodeDice(nDice, dieSize, add)#1
|
||||
predef fatal(msg)#1
|
||||
predef finalWin()#0
|
||||
predef finishString(isPlural)#1
|
||||
predef flipToPage1()#0
|
||||
predef getCharResponse()#1
|
||||
@ -65,14 +65,15 @@ import gamelib
|
||||
predef initPlayerXP(player)#0
|
||||
predef loadFrameImg(img)#0
|
||||
predef loadMainFrameImg()#0
|
||||
predef lookupResourcePart(sectionNum, resourceNum)#1
|
||||
predef makeModifier(name, value)#1
|
||||
predef max(a, b)#1
|
||||
predef memcpy(pSrc, pDst, len)#0
|
||||
predef min(a, b)#1
|
||||
predef mmgr(cmd, wordParam)#1
|
||||
predef moveWayBackward()#1
|
||||
predef numToPlayer(num)#1
|
||||
predef parseDec(str)#1
|
||||
predef parseDecWithDefault(str, default)#1
|
||||
predef partyHasPlayer(playerName)#1
|
||||
predef pause(count)#0
|
||||
predef payGold(amount)#1
|
||||
@ -93,7 +94,6 @@ import gamelib
|
||||
predef rawDisplayf3(fmt, arg1, arg2, arg3)#0
|
||||
predef rawDisplayStr(str)#0
|
||||
predef rdkey()#1
|
||||
predef readStr()#1
|
||||
predef removeFromList(pList, toRemove)#0
|
||||
predef removePlayerFromParty(playerName)#0
|
||||
predef rightJustifyNum(num, rightX)#0
|
||||
@ -116,7 +116,6 @@ import gamelib
|
||||
predef setMap(is3D, num, x, y, dir)#0
|
||||
predef setMapWindow()#0
|
||||
predef setBigWindow()#0
|
||||
predef setPlural(flg)#0
|
||||
predef setPortrait(portraitNum)#0
|
||||
predef setScriptInfo(mapName, trigTbl, wdt, hgt)#0
|
||||
predef setSky(num)#0
|
||||
@ -147,6 +146,8 @@ import gamelib
|
||||
word groundNum
|
||||
byte portraitNum
|
||||
word pGodModule
|
||||
word typeHash
|
||||
byte isPlural
|
||||
|
||||
/////////// Shared string constants //////////////
|
||||
|
||||
|
@ -60,7 +60,6 @@ predef doRender()#0
|
||||
predef playerDeath()#0
|
||||
predef startGame(ask)#0
|
||||
predef showAnimFrame()#0
|
||||
predef finalWin()#0
|
||||
predef showParty()#0
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -77,12 +76,12 @@ byte renderLoaded = FALSE
|
||||
byte texturesLoaded = FALSE
|
||||
byte textDrawn = FALSE
|
||||
byte textClearCountdown = 0
|
||||
byte isPlural = FALSE
|
||||
byte skipEncounterCheck = FALSE
|
||||
export byte isPlural = 0 // valid values: 0 or $40
|
||||
byte inScript = FALSE
|
||||
|
||||
export word skyNum = 9
|
||||
export word groundNum = 10
|
||||
export byte portraitNum = 1
|
||||
export byte portraitNum = 0
|
||||
|
||||
word triggerOriginX, triggerOriginY
|
||||
word triggerTbl
|
||||
@ -97,6 +96,7 @@ word pResourceIndex = NULL
|
||||
word pGlobalTileset = NULL
|
||||
byte curMapPartition = 0
|
||||
export word pGodModule = NULL
|
||||
export word typeHash = 0
|
||||
|
||||
// Queue setMap / teleport / start_encounter, since otherwise script might be replaced while executing
|
||||
byte q_mapIs3D = 0
|
||||
@ -405,78 +405,73 @@ export asm finishString(isPlural)#1
|
||||
sta cswl
|
||||
lda #$FD
|
||||
sta cswh
|
||||
bit fixedRTS; V flag for prev-is-punctuation
|
||||
ldy #1 ; dest offset in Y
|
||||
ldx #1 ; source offset in X
|
||||
clv ; V flag for prev-is-alpha
|
||||
ldy #0 ; dest offset in Y (will be incremented before store)
|
||||
ldx #0 ; source offset in X (will be incremented before load)
|
||||
cpx inbuf
|
||||
beq + ; only process if string has at least 1 char
|
||||
bcs .done
|
||||
+ sty tmp+1 ; offset of last punctuation
|
||||
beq .done ; failsafe: handle zero-length string
|
||||
.fetch
|
||||
lda inbuf,x
|
||||
cmp #"("
|
||||
inx
|
||||
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
|
||||
bvs .notpar ; skip paren processing right punctuation
|
||||
lda tmp ; check isPlural flag
|
||||
bne .plurpr
|
||||
- lda inbuf,x ; it's singular, so skip everything in parens
|
||||
cmp #")"
|
||||
beq .next
|
||||
bvc .notpar ; but only parens directly after alpha char, e.g. preserving "Happy (and safe)."
|
||||
|
||||
dey ; undo copy of the paren
|
||||
stx tmp+1 ; save position in input
|
||||
dex ; needed for failsafe operation
|
||||
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
|
||||
cpx inbuf
|
||||
bne -
|
||||
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
|
||||
lda inbuf,x
|
||||
cmp #"/"
|
||||
bne .notsl
|
||||
bvs .notsl ; skip slash processing right after punctuation
|
||||
lda tmp ; check isPlural flag
|
||||
bne .plursl
|
||||
- inx ; loop that skips plural form
|
||||
cpx inbuf
|
||||
bne +
|
||||
php
|
||||
pla
|
||||
eor #$40 ; flip V flag, meaning singular text is before slash, plural after.
|
||||
pha
|
||||
plp
|
||||
+ cmp #")" ; scan until ending paren
|
||||
beq +
|
||||
bcs .done ; handle end of string
|
||||
+ lda inbuf,x
|
||||
cmp #"A" ; eat letters (and stop when we hit punctuation)
|
||||
bcs -
|
||||
bcc .store ; copy the ending punctuation and continue normal processing
|
||||
.plursl
|
||||
ldy tmp+1 ; erase singular form by backing up to prev punc
|
||||
iny ; plus 1 to retain prev punc
|
||||
bne .next ; resume regular copying of the plural form
|
||||
|
||||
.notsl
|
||||
cmp #"A" ; if <= ASCII "A", consider it punctuation
|
||||
bcc +
|
||||
clv ; clear last-is-punc flag
|
||||
bvc .store ; always taken
|
||||
+ bit fixedRTS; set prev-is-punc flag
|
||||
sty tmp+1 ; save dest offset of last punctuation
|
||||
|
||||
.store
|
||||
sta inbuf,y ; save to dest
|
||||
cpx inbuf
|
||||
bcc .findsl ; loop to scan next char
|
||||
bcs .done ; failsafe: handle missing end-paren (always taken)
|
||||
+ ldx tmp+1 ; get back to start of parens
|
||||
; copy mode flag is now in V: if slash present, single=copy, plural=nocopy
|
||||
; if no slash: single=nocopy, plural=copy
|
||||
.plup
|
||||
inx
|
||||
lda inbuf,x
|
||||
cmp #"/"
|
||||
bne +
|
||||
php
|
||||
pla
|
||||
eor #$40 ; flip from copying to not-copying, or vice-versa
|
||||
pha
|
||||
plp
|
||||
bcs .plup ; always taken
|
||||
+ cmp #")"
|
||||
beq .notpar ; stop at closing paren
|
||||
bvc .plup ; if not in copy mode, skip copy
|
||||
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
|
||||
inx
|
||||
cpx inbuf ; compare src offset to length
|
||||
bcc .fetch ; loop while less than
|
||||
beq .fetch ; or equal
|
||||
|
||||
.done
|
||||
dey
|
||||
sty inbuf ; save new length
|
||||
lda #<inbuf ; return pointer to string
|
||||
ldy #>inbuf
|
||||
@ -729,7 +724,7 @@ end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Print a carriage return
|
||||
asm crout()#0
|
||||
export asm crout()#0
|
||||
+asmPlasmNoRet 0
|
||||
lda #$8D
|
||||
jmp _safeCout
|
||||
@ -743,28 +738,6 @@ export asm beep()#0
|
||||
rts
|
||||
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
|
||||
export asm mmgr(cmd, wordParam)#1
|
||||
@ -998,7 +971,8 @@ export asm streqi(a, b)#1
|
||||
bne -- ; abort on inequality
|
||||
dex
|
||||
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
|
||||
end
|
||||
|
||||
@ -1098,12 +1072,6 @@ export def getStringResponse()#1
|
||||
return mmgr(HEAP_INTERN, $200)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Setter functions for library use
|
||||
export def setPlural(flg)#0
|
||||
isPlural = flg
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Convert signed decimal to string in decimalBuf (@decimalBuf returned)
|
||||
def convertDec(n)#1
|
||||
@ -1230,14 +1198,6 @@ export def parseDec(str)#1
|
||||
return n
|
||||
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
|
||||
export def getUpperKey()#1
|
||||
@ -1316,7 +1276,7 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def rollDiceWithLuck(encoded, luck)#1
|
||||
byte i, nDice, dieSize, add, droll, result
|
||||
nDice = encoded >> 12
|
||||
nDice = (encoded >> 12) & $F // must mask off replicated hi-bits
|
||||
dieSize = (encoded >> 8) & $F
|
||||
add = encoded & $F
|
||||
result = add
|
||||
@ -1331,8 +1291,7 @@ export def rollDiceWithLuck(encoded, luck)#1
|
||||
droll = min(droll, (rand16() % dieSize) + 1)
|
||||
fin
|
||||
fin
|
||||
add = (rand16() % dieSize) + 1
|
||||
result = result + add
|
||||
result = result + droll
|
||||
next
|
||||
return result
|
||||
end
|
||||
@ -1361,13 +1320,13 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Look up the partition for a resource.
|
||||
// sectioNum: 1=map2d, 2=map3d, 3=portrait
|
||||
def lookupResourcePart(sectionNum, resourceNum)#1
|
||||
export def lookupResourcePart(sectionNum, resourceNum)#1
|
||||
word ptr
|
||||
byte n
|
||||
|
||||
// Skip to the requested section
|
||||
// Skip to the requested section (starting just after version num)
|
||||
ptr = pResourceIndex
|
||||
while sectionNum > 1
|
||||
while sectionNum > 0
|
||||
ptr = ptr + readAuxByte(ptr) + 1
|
||||
sectionNum--
|
||||
loop
|
||||
@ -1381,17 +1340,13 @@ def lookupResourcePart(sectionNum, resourceNum)#1
|
||||
if curMapPartition > 0; return curMapPartition; fin
|
||||
return 2
|
||||
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
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Set the sky color (relevant to 3D display only)
|
||||
export def setSky(num)#0
|
||||
// hack for end-game screen
|
||||
if num == 99
|
||||
finalWin()
|
||||
fin
|
||||
skyNum = num
|
||||
setColor(0, skyNum)
|
||||
needRender = TRUE
|
||||
@ -1405,6 +1360,36 @@ export def setGround(num)#0
|
||||
needRender = TRUE
|
||||
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.
|
||||
export def loadFrameImg(img)#0
|
||||
@ -1435,6 +1420,9 @@ export def loadFrameImg(img)#0
|
||||
|
||||
// And show the first frame of the screen image
|
||||
showAnimFrame()
|
||||
|
||||
// Brand the image with the version number
|
||||
printVersion()
|
||||
else
|
||||
curFullscreenImg = NULL
|
||||
anyAnims = FALSE
|
||||
@ -1591,6 +1579,9 @@ export def scriptEvent(event, param)#0
|
||||
word script
|
||||
if !nMapScripts; return; fin
|
||||
|
||||
if inScript; return; fin // avoid doing scripted events inside other scripts
|
||||
inScript = TRUE
|
||||
|
||||
setWindow2()
|
||||
|
||||
textDrawn = FALSE
|
||||
@ -1606,6 +1597,8 @@ export def scriptEvent(event, param)#0
|
||||
fin
|
||||
next
|
||||
|
||||
inScript = FALSE
|
||||
|
||||
if textDrawn
|
||||
textClearCountdown = 3
|
||||
if mapIs3D and texturesLoaded; copyWindow(); fin
|
||||
@ -1650,11 +1643,15 @@ end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def loadMainFrameImg()#0
|
||||
loadFrameImg(mapIs3D+2)
|
||||
if curFullscreenImg
|
||||
auxMmgr(FREE_MEMORY, curFullscreenImg)
|
||||
curFullscreenImg = NULL
|
||||
fin
|
||||
loadFrameImg(mapIs3D+2)
|
||||
if curFullscreenImg
|
||||
auxMmgr(FREE_MEMORY, curFullscreenImg) // we don't allow animated main frames, so save memory
|
||||
curFullscreenImg = NULL
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1796,15 +1793,17 @@ def doRender()#0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Advance one step forward (works for either 3D or 2D maps)
|
||||
def moveForward()#1
|
||||
def moveInternal(facingDir, moveDir, beepOK)#1
|
||||
byte val
|
||||
word x, y
|
||||
|
||||
setDir(moveDir)
|
||||
val = advance()
|
||||
setDir(facingDir)
|
||||
|
||||
// If not blocked, render at the new position.
|
||||
if val == 0
|
||||
beep()
|
||||
if beepOK and !inScript; beep(); fin // don't beep for scripted moves
|
||||
else
|
||||
if !mapIs3D
|
||||
doRender()
|
||||
@ -1813,7 +1812,7 @@ def moveForward()#1
|
||||
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
|
||||
scriptEvent(@S_LEAVE, NULL)
|
||||
nMapScripts = 0
|
||||
@ -1825,14 +1824,22 @@ def moveForward()#1
|
||||
scanScripts(x, y)
|
||||
if nMapScripts
|
||||
scriptEvent(@S_ENTER, NULL)
|
||||
elsif global=>p_encounterZones and !skipEncounterCheck
|
||||
elsif global=>p_encounterZones
|
||||
checkEncounter(x, y, FALSE)
|
||||
fin
|
||||
elsif val >= 2 and global=>p_encounterZones and !skipEncounterCheck
|
||||
elsif val >= 2 and global=>p_encounterZones
|
||||
getPos(@x, @y)
|
||||
checkEncounter(x, y, FALSE)
|
||||
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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1847,23 +1854,25 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Move backward one step (3D mode). Also actually works in 2D mode.
|
||||
def moveBackward()#1
|
||||
adjustDir(8)
|
||||
moveForward()
|
||||
return adjustDir(8)
|
||||
byte facingDir, moveDir
|
||||
facingDir = getDir()
|
||||
moveDir = (facingDir + 8) & 15
|
||||
moveInternal(facingDir, moveDir, TRUE)
|
||||
return 0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
export def moveWayBackward()#1
|
||||
adjustDir(8)
|
||||
skipEncounterCheck = TRUE
|
||||
moveForward()
|
||||
byte facingDir, moveDir
|
||||
facingDir = getDir()
|
||||
moveDir = (facingDir + 8) & 15
|
||||
moveInternal(facingDir, moveDir, FALSE)
|
||||
if mapIs3D
|
||||
moveForward()
|
||||
moveInternal(facingDir, moveDir, FALSE)
|
||||
fin
|
||||
skipEncounterCheck = FALSE
|
||||
return adjustDir(8)
|
||||
return 0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1885,17 +1894,19 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Sidestep to the right (3D mode)
|
||||
def strafeRight()#1
|
||||
adjustDir(4)
|
||||
moveForward()
|
||||
return adjustDir(-4)
|
||||
byte facingDir, moveDir
|
||||
facingDir = getDir()
|
||||
moveDir = (facingDir + 4) & 15
|
||||
return moveInternal(facingDir, moveDir, FALSE)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Sidestep to the left (3D mode)
|
||||
def strafeLeft()#1
|
||||
adjustDir(-4)
|
||||
moveForward()
|
||||
return adjustDir(4)
|
||||
byte facingDir, moveDir
|
||||
facingDir = getDir()
|
||||
moveDir = (facingDir - 4) & 15
|
||||
return moveInternal(facingDir, moveDir, FALSE)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2105,7 +2116,9 @@ export def getYN()#1
|
||||
if key == 'Y'
|
||||
return 1
|
||||
elsif key == 'N'
|
||||
clearTextWindow()
|
||||
if frameLoaded
|
||||
clearTextWindow()
|
||||
fin
|
||||
break
|
||||
fin
|
||||
beep()
|
||||
@ -2185,7 +2198,8 @@ export def setPortrait(portraitNum)#0
|
||||
|
||||
// Load the portrait image and display it
|
||||
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)
|
||||
curPortrait = auxMmgr(QUEUE_LOAD, portraitNum<<8 | RES_TYPE_PORTRAIT)
|
||||
curPortraitNum = portraitNum
|
||||
@ -2303,12 +2317,6 @@ def loadEngine(moduleNum)#1
|
||||
flipToPage1()
|
||||
mmgr(START_LOAD, 1) // code is in partition 1
|
||||
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)
|
||||
return curEngine() // return function table
|
||||
end
|
||||
@ -2323,7 +2331,12 @@ def returnFromEngine(render)#0
|
||||
if renderLoaded; texControl(1); texturesLoaded = TRUE; fin
|
||||
mapNameHash = 0; showMapName(global=>s_mapName)
|
||||
clearTextWindow()
|
||||
if render; doRender(); fin
|
||||
if render
|
||||
doRender()
|
||||
else
|
||||
needRender = TRUE
|
||||
fin
|
||||
if mapIs3D; showCompassDir(getDir()); fin
|
||||
showParty()
|
||||
setWindow2() // in case we're mid-script
|
||||
fin
|
||||
@ -2357,13 +2370,21 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Load the Party engine and show data for the given player
|
||||
def showPlayerSheet(num)#1
|
||||
word pItemToUse
|
||||
word pItemToUse, oldFlg
|
||||
if num+1 > countList(global=>p_players); beep; return 0; fin
|
||||
pItemToUse = loadEngine(MOD_PARTY)=>party_showPlayerSheet(num)
|
||||
returnFromEngine(TRUE)
|
||||
// General 'use' handled here in case it triggers graphical effects
|
||||
if pItemToUse
|
||||
oldFlg = textDrawn
|
||||
textDrawn = FALSE
|
||||
scriptEvent(@S_USE, pItemToUse=>s_name)
|
||||
if !textDrawn
|
||||
displayStr("\nNothing happened.")
|
||||
textDrawn = TRUE
|
||||
else
|
||||
textDrawn = oldFlg
|
||||
fin
|
||||
fin
|
||||
return 0
|
||||
end
|
||||
@ -2391,6 +2412,7 @@ def levelUp()#1
|
||||
return 0
|
||||
fin
|
||||
player = player=>p_nextObj
|
||||
n++
|
||||
loop
|
||||
beep
|
||||
return 0
|
||||
@ -2433,13 +2455,14 @@ def doCombat(mapCode, backUpOnFlee)#1
|
||||
// Handled in a separate module. Clear enemies out of the heap when finished.
|
||||
result = loadEngine(MOD_COMBAT)=>combat_zoneEncounter(mapCode)
|
||||
global=>p_enemyGroups = NULL
|
||||
mmgr(CHECK_MEM, 0)
|
||||
mmgr(HEAP_COLLECT, 0)
|
||||
|
||||
if (result == -99)
|
||||
playerDeath()
|
||||
return 0
|
||||
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 !result and backUpOnFlee
|
||||
@ -2455,6 +2478,9 @@ export def checkEncounter(x, y, force)#0
|
||||
word p_bestZone, bestDist
|
||||
word d
|
||||
|
||||
// Don't check for encounter during scripted move
|
||||
if inScript; return; fin
|
||||
|
||||
// Find the zone that's closest, but not too far.
|
||||
bestDist = INT_MAX
|
||||
p_bestZone = NULL
|
||||
@ -2511,6 +2537,7 @@ def toggleGodMode()#1
|
||||
if ^kbd == $84 // ctrl-D
|
||||
^kbdStrobe
|
||||
global->b_godmode = !global->b_godmode
|
||||
flipToPage1()
|
||||
clearTextWindow()
|
||||
displayf1("gm:%d\n", global->b_godmode & 1)
|
||||
beep; beep
|
||||
@ -2521,15 +2548,6 @@ def toggleGodMode()#1
|
||||
return 0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def finalWin()#0
|
||||
flipToPage1()
|
||||
loadFrameImg(4) // total hack
|
||||
while 1 // 1 infinite loop
|
||||
getUpperKey()
|
||||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def setCmd(key, func)#0
|
||||
cmdTbl[key] = func
|
||||
@ -2641,10 +2659,6 @@ def loadTitle()#0
|
||||
auxMmgr(SET_MEM_TARGET, expandVec)
|
||||
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)
|
||||
|
||||
// Tell the font engine where to find its font
|
||||
@ -2663,13 +2677,20 @@ def loadTitle()#0
|
||||
auxMmgr(SET_MEM_TARGET, expandVec)
|
||||
auxMmgr(REQUEST_MEMORY, expanderSize)
|
||||
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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Set up the small-object heap. Set loadedSize to zero on initial, or non-zero for loaded game.
|
||||
export def initHeap(loadedSize)#0
|
||||
byte i
|
||||
word typeHash
|
||||
|
||||
if !heapLocked
|
||||
mmgr(SET_MEM_TARGET, HEAP_BOTTOM)
|
||||
@ -2687,12 +2708,8 @@ export def initHeap(loadedSize)#0
|
||||
mmgr(HEAP_ADD_TYPE, typeTbls[i])
|
||||
i = i+1
|
||||
loop
|
||||
typeHash = hashBuffer(@typeTbl_Global, @typeTbls - @typeTbl_Global) ^ HEAP_BOTTOM
|
||||
if loadedSize <> 0
|
||||
global = HEAP_BOTTOM
|
||||
if global=>w_typeHash <> typeHash
|
||||
fatal("Incompatible saved game")
|
||||
fin
|
||||
else
|
||||
global = mmgr(HEAP_ALLOC, TYPE_GLOBAL)
|
||||
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.
|
||||
// Also handles incrementing stackable items.
|
||||
export def addUnique(pList, p_thing)#1
|
||||
if !scanForNamedObj(*pList, p_thing=>s_name)
|
||||
addToList(pList, p_thing)
|
||||
return TRUE
|
||||
word p_existing
|
||||
|
||||
// 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
|
||||
fin
|
||||
return FALSE // already have one, and it's not stackable
|
||||
fin
|
||||
return FALSE
|
||||
|
||||
// Otherwise, add a new item
|
||||
addToList(pList, p_thing)
|
||||
return TRUE
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2868,7 +2896,8 @@ end
|
||||
export def addPlayerToParty(playerFuncNum)#0
|
||||
word p
|
||||
if countList(global=>p_players) == MAX_PARTY
|
||||
displayStr("Party too large.")
|
||||
rawDisplayStr("Party too large.")
|
||||
beep
|
||||
return
|
||||
fin
|
||||
p = createAndAddUnique(MOD_GEN_PLAYERS, playerFuncNum, @global=>p_players)
|
||||
@ -2882,7 +2911,12 @@ export def removeNamed(name, pList)#0
|
||||
word p_thing
|
||||
p_thing = scanForNamedObj(*pList, name)
|
||||
if p_thing
|
||||
removeFromList(pList, 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)
|
||||
fin
|
||||
else
|
||||
printf1("Warning: couldn't find '%s' to remove.\n", name)
|
||||
fin
|
||||
@ -2912,29 +2946,29 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def getStat(player, statName)#1
|
||||
word pSkill
|
||||
when statName
|
||||
is @S_INTELLIGENCE; return player->b_intelligence
|
||||
is @S_STRENGTH; return player->b_strength
|
||||
is @S_AGILITY; return player->b_agility
|
||||
is @S_STAMINA; return player->b_stamina
|
||||
is @S_CHARISMA; return player->b_charisma
|
||||
is @S_SPIRIT; return player->b_spirit
|
||||
is @S_LUCK; return player->b_luck
|
||||
is @S_HEALTH; return player=>w_health
|
||||
is @S_MAX_HEALTH; return player=>w_maxHealth
|
||||
is @S_AIMING; return player->b_aiming
|
||||
is @S_HAND_TO_HAND; return player->b_handToHand
|
||||
is @S_DODGING; return player->b_dodging
|
||||
is @S_GOLD; return global=>w_gold
|
||||
is @S_XP; return player=>w_curXP
|
||||
is @S_SP; return player->b_skillPoints
|
||||
when 1
|
||||
is streqi(statName, @S_INTELLIGENCE); return player->b_intelligence
|
||||
is streqi(statName, @S_STRENGTH); return player->b_strength
|
||||
is streqi(statName, @S_AGILITY); return player->b_agility
|
||||
is streqi(statName, @S_STAMINA); return player->b_stamina
|
||||
is streqi(statName, @S_CHARISMA); return player->b_charisma
|
||||
is streqi(statName, @S_SPIRIT); return player->b_spirit
|
||||
is streqi(statName, @S_LUCK); return player->b_luck
|
||||
is streqi(statName, @S_HEALTH); return player=>w_health
|
||||
is streqi(statName, @S_MAX_HEALTH); return player=>w_maxHealth
|
||||
is streqi(statName, @S_AIMING); return player->b_aiming
|
||||
is streqi(statName, @S_HAND_TO_HAND); return player->b_handToHand
|
||||
is streqi(statName, @S_DODGING); return player->b_dodging
|
||||
is streqi(statName, @S_GOLD); return global=>w_gold
|
||||
is streqi(statName, @S_XP); return player=>w_curXP
|
||||
is streqi(statName, @S_SP); return player->b_skillPoints
|
||||
wend
|
||||
pSkill = player=>p_skills
|
||||
while pSkill
|
||||
if streqi(statName, pSkill=>s_name); return pSkill=>w_modValue; fin
|
||||
pSkill = pSkill=>p_nextObj
|
||||
loop
|
||||
puts(statName); return fatal("Unknown stat")
|
||||
puts(statName); return fatal("getStat")
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2953,22 +2987,22 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def setStat(player, statName, val)#0
|
||||
word pSkill
|
||||
when statName
|
||||
is @S_INTELLIGENCE; player->b_intelligence = clampByte(val); break
|
||||
is @S_STRENGTH; player->b_strength = clampByte(val); break
|
||||
is @S_AGILITY; player->b_agility = clampByte(val); break
|
||||
is @S_STAMINA; player->b_stamina = clampByte(val); break
|
||||
is @S_CHARISMA; player->b_charisma = clampByte(val); break
|
||||
is @S_SPIRIT; player->b_spirit = clampByte(val); break
|
||||
is @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 @S_MAX_HEALTH; player=>w_maxHealth = max(0, val); break
|
||||
is @S_AIMING; player->b_aiming = clampByte(val); break
|
||||
is @S_HAND_TO_HAND; player->b_handToHand = clampByte(val); break
|
||||
is @S_DODGING; player->b_dodging = clampByte(val); break
|
||||
is @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 @S_SP; player->b_skillPoints = max(0, val); needShowParty = TRUE; break
|
||||
when 1
|
||||
is streqi(statName, @S_INTELLIGENCE); player->b_intelligence = clampByte(val); break
|
||||
is streqi(statName, @S_STRENGTH); player->b_strength = clampByte(val); break
|
||||
is streqi(statName, @S_AGILITY); player->b_agility = clampByte(val); break
|
||||
is streqi(statName, @S_STAMINA); player->b_stamina = clampByte(val); break
|
||||
is streqi(statName, @S_CHARISMA); player->b_charisma = clampByte(val); break
|
||||
is streqi(statName, @S_SPIRIT); player->b_spirit = clampByte(val); break
|
||||
is streqi(statName, @S_LUCK); player->b_luck = clampByte(val); break
|
||||
is streqi(statName, @S_HEALTH); player=>w_health = max(0, min(player=>w_maxHealth, val)); needShowParty = TRUE; break
|
||||
is streqi(statName, @S_MAX_HEALTH); player=>w_maxHealth = max(0, val); break
|
||||
is streqi(statName, @S_AIMING); player->b_aiming = clampByte(val); break
|
||||
is streqi(statName, @S_HAND_TO_HAND); player->b_handToHand = clampByte(val); break
|
||||
is streqi(statName, @S_DODGING); player->b_dodging = clampByte(val); break
|
||||
is streqi(statName, @S_GOLD); global=>w_gold = max(0, val); needShowParty = TRUE; break
|
||||
is streqi(statName, @S_XP); player=>w_curXP = max(player=>w_curXP, val); needShowParty = TRUE; break
|
||||
is streqi(statName, @S_SP); player->b_skillPoints = max(0, val); needShowParty = TRUE; break
|
||||
otherwise
|
||||
pSkill = player=>p_skills
|
||||
while pSkill
|
||||
@ -2978,33 +3012,28 @@ export def setStat(player, statName, val)#0
|
||||
fin
|
||||
pSkill = pSkill=>p_nextObj
|
||||
loop
|
||||
puts(statName); fatal("Unknown stat")
|
||||
puts(statName); fatal("setStat")
|
||||
wend
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def setGameFlag(flagName, val)#0
|
||||
word p_flag
|
||||
p_flag = scanForNamedObj(global=>p_gameFlags, flagName)
|
||||
if p_flag
|
||||
if val == 0 // setting flag to zero removes it
|
||||
removeFromList(@global=>p_gameFlags, p_flag)
|
||||
else
|
||||
p_flag=>w_modValue = val
|
||||
fin
|
||||
elsif val <> 0
|
||||
addToList(@global=>p_gameFlags, makeModifier(flagName, val))
|
||||
export def setGameFlag(flagNum, val)#0
|
||||
byte byteNum, mask
|
||||
byteNum = flagNum >> 3
|
||||
mask = 1<<(flagNum & 7)
|
||||
if val
|
||||
global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] | mask
|
||||
else
|
||||
global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] & ~mask
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def getGameFlag(flagName)#1
|
||||
word p_flag
|
||||
p_flag = scanForNamedObj(global=>p_gameFlags, flagName)
|
||||
if p_flag
|
||||
return p_flag=>w_modValue
|
||||
fin
|
||||
return 0
|
||||
export def getGameFlag(flagNum)#1
|
||||
byte byteNum, mask
|
||||
byteNum = flagNum >> 3
|
||||
mask = 1<<(flagNum & 7)
|
||||
return global->ba_gameFlags[byteNum] & mask
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -3044,10 +3073,23 @@ export def buySell(storeCode, profitRatio)#0
|
||||
if portrait; setPortrait(portrait); fin
|
||||
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
|
||||
word p_module
|
||||
|
||||
typeHash = hashBuffer(@typeTbl_Global, @typeTbls - @typeTbl_Global) ^ HEAP_BOTTOM
|
||||
|
||||
// Create a new game or load an existing one
|
||||
mmgr(START_LOAD, 1) // code is in partition 1
|
||||
p_module = mmgr(QUEUE_LOAD, MOD_DISKOPS<<8 | RES_TYPE_MODULE)
|
||||
|
@ -16,6 +16,7 @@ include "gen_images.plh"
|
||||
include "gen_modules.plh"
|
||||
include "gen_items.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
|
||||
// in the same order as the constants are defined in the the header.
|
||||
@ -24,6 +25,56 @@ predef _addItem(player)#1
|
||||
predef _addPlayer()#1
|
||||
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
|
||||
@ -32,12 +83,10 @@ def kbdTeleport()#1
|
||||
word x, y
|
||||
byte dir
|
||||
|
||||
flipToPage1()
|
||||
^$c053
|
||||
if ^$25 < 23; ^$25 = 23; fin
|
||||
splitScreenMode()
|
||||
getPos(@x, @y)
|
||||
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)
|
||||
d3 = parseDecWithDefault(readStr(), mapIs3D)
|
||||
@ -62,11 +111,9 @@ def showPos()#1
|
||||
word x, y
|
||||
byte dir
|
||||
|
||||
flipToPage1()
|
||||
^$c053
|
||||
if ^$25 < 23; ^$25 = 23; fin
|
||||
splitScreenMode()
|
||||
getPos(@x, @y)
|
||||
printf2("\nX=%d Y=%d ", x, y)
|
||||
printf2("X=%d Y=%d ", x, y)
|
||||
if mapIs3D
|
||||
printf3("Facing=%d Sky=%d Ground=%d", getDir(), skyNum, groundNum)
|
||||
fin
|
||||
@ -77,19 +124,28 @@ def showPos()#1
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def nextPortrait()#1
|
||||
portraitNum = portraitNum + 1
|
||||
if portraitNum > PO_LAST; portraitNum = 1; fin
|
||||
def advPortrait(dir)#1
|
||||
while TRUE
|
||||
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)
|
||||
return 0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def nextPortrait()#1
|
||||
return advPortrait(1)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def prevPortrait()#1
|
||||
portraitNum = portraitNum - 1
|
||||
if portraitNum < 1; portraitNum = PO_LAST; fin
|
||||
setPortrait(portraitNum)
|
||||
return 0
|
||||
return advPortrait(-1)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -126,6 +182,45 @@ def nextGround()#1
|
||||
return 0
|
||||
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
|
||||
// install cheat commands
|
||||
@ -137,7 +232,7 @@ def _setCheatCmds()#1
|
||||
setCmd('Y', @nextSky)
|
||||
setCmd('G', @nextGround)
|
||||
setCmd('&', @printMem)
|
||||
setCmd('_', @finalWin)
|
||||
setCmd('^', @editFlags)
|
||||
return 0
|
||||
end
|
||||
|
||||
@ -176,10 +271,14 @@ def selectThing(moduleNum, nThings, nSkip, prompt)#1
|
||||
nFunc = -1
|
||||
textHome()
|
||||
fin
|
||||
elsif moduleNum == MOD_GEN_PLAYERS
|
||||
// Players are big, so need collect each time
|
||||
mmgr(HEAP_COLLECT, 0)
|
||||
fin
|
||||
next
|
||||
|
||||
mmgr(FREE_MEMORY, pModule)
|
||||
^$c050
|
||||
return nFunc
|
||||
end
|
||||
|
||||
@ -188,11 +287,7 @@ end
|
||||
def _addItem(player)#1
|
||||
word funcNum
|
||||
funcNum = selectThing(MOD_GEN_ITEMS, NUM_ITEMS, 2, "Add item #: ")
|
||||
if funcNum >= 0
|
||||
giveItemToPlayer(player, funcNum)
|
||||
rdkey()
|
||||
fin
|
||||
^$c050
|
||||
if funcNum >= 0; giveItemToPlayer(player, funcNum); fin
|
||||
return 0
|
||||
end
|
||||
|
||||
@ -201,11 +296,7 @@ end
|
||||
def _addPlayer()#1
|
||||
word funcNum
|
||||
funcNum = selectThing(MOD_GEN_PLAYERS, NUM_PLAYERS, 1, "Add player #: ")
|
||||
if funcNum >= 0
|
||||
addPlayerToParty(funcNum)
|
||||
rdkey()
|
||||
fin
|
||||
^$c050
|
||||
if funcNum >= 0; addPlayerToParty(funcNum); fin
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -68,6 +68,14 @@ def travFind2(pObj, val, func)
|
||||
return NULL
|
||||
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
|
||||
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
|
||||
while item
|
||||
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
|
||||
break
|
||||
fin
|
||||
@ -97,7 +105,7 @@ end
|
||||
def showColumnTitle(x, title, page, nPages)#0
|
||||
rawDisplayf2("^V000\n^J^J^L^J^T%D%s", x, title)
|
||||
if nPages > 1
|
||||
rawDisplayf2(" - p.%d/%d", page, nPages)
|
||||
rawDisplayf2(" p.%d/%d", page, nPages)
|
||||
fin
|
||||
rawDisplayStr("^N\n^J^J")
|
||||
end
|
||||
@ -125,10 +133,8 @@ def showInventory(player, page, select)#1
|
||||
if !first; displayChar('\n'); fin
|
||||
first = FALSE
|
||||
|
||||
if item->t_type == TYPE_WEAPON or item->t_type == TYPE_ARMOR
|
||||
if item->b_flags & ITEM_FLAG_EQUIP
|
||||
rawDisplayStr("*")
|
||||
fin
|
||||
if isEquipped(item)
|
||||
rawDisplayStr("*")
|
||||
fin
|
||||
|
||||
rawDisplayf2("^T%D%c.", INVLBL_X, 'A' + s_item)
|
||||
@ -142,17 +148,6 @@ def showInventory(player, page, select)#1
|
||||
return s_item
|
||||
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
|
||||
byte n, d, p
|
||||
n = (dice >> 12) & $0F
|
||||
@ -196,7 +191,7 @@ def showDerived(player)#0
|
||||
// Get weapon
|
||||
weapon = player=>p_items
|
||||
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
|
||||
loop
|
||||
|
||||
@ -339,12 +334,8 @@ def showInvMenu(player, totalItems, itemPage, itemsOnPage)#0
|
||||
rawDisplayf1("Item [A-%c], ", itemsOnPage-1+'A')
|
||||
if totalItems > INV_ROWS
|
||||
rawDisplayStr("Pg [")
|
||||
if totalItems > (itemPage + 1) * INV_ROWS
|
||||
rawDisplayStr(">")
|
||||
fin
|
||||
if itemPage
|
||||
rawDisplayStr("<")
|
||||
fin
|
||||
if totalItems > (itemPage + 1) * INV_ROWS; rawDisplayStr(">"); fin
|
||||
if itemPage; rawDisplayStr("<"); fin
|
||||
rawDisplayStr("], ")
|
||||
fin
|
||||
fin
|
||||
@ -372,18 +363,37 @@ def showSkillsMenu()#0
|
||||
rawDisplayStr("X:Inv or [Esc]")
|
||||
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
|
||||
def showItemMenu(item)#0
|
||||
byte type
|
||||
clearMenuRect()
|
||||
type = item->t_type
|
||||
if type == TYPE_ARMOR or type == TYPE_WEAPON
|
||||
rawDisplayStr("E)quip/unequip, ")
|
||||
fin
|
||||
if type == TYPE_ITEM
|
||||
if isEquipped(item)
|
||||
rawDisplayStr("U)nequip, ")
|
||||
else
|
||||
rawDisplayStr("E)quip, ")
|
||||
fin
|
||||
elsif type == TYPE_PLAIN_ITEM or type == TYPE_FANCY_ITEM
|
||||
rawDisplayStr("U)se, ")
|
||||
if isSplittable(item); rawDisplayStr("S)plit, "); fin
|
||||
if isJoinable(item); rawDisplayStr("J)oin, "); 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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -398,6 +408,7 @@ def clearMainRect()#0
|
||||
setWindow(BIGWIN_TOP+9, BIGWIN_BOTTOM-10, BIGWIN_LEFT, BIGWIN_RIGHT)
|
||||
clearWindow()
|
||||
setBigWindow()
|
||||
rawDisplayStr("^V000\n^J^J^J")
|
||||
end
|
||||
|
||||
// Equip/unequip an item.
|
||||
@ -408,26 +419,151 @@ def doEquip(player, item)#0
|
||||
calcPlayerArmor(player)
|
||||
end
|
||||
|
||||
def choosePlayer(disp, chooseNum, avoid)
|
||||
word player
|
||||
byte num
|
||||
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()
|
||||
clearMainRect()
|
||||
rawDisplayStr("Done.")
|
||||
pause(800)
|
||||
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
|
||||
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
|
||||
if item=>p_modifiers
|
||||
if streqi(item=>p_modifiers=>s_name, @S_HEALTH)
|
||||
clearMenuRect()
|
||||
clearMainRect()
|
||||
rawDisplayStr("^V000\n^J^J^J")
|
||||
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)
|
||||
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("Healed to %d/%d", player=>w_health, player=>w_maxHealth)
|
||||
rawDisplayf2(" from %d to %d.", oldVal, newVal)
|
||||
else
|
||||
rawDisplayStr("No healing needed.")
|
||||
rawDisplayStr(" already at the limit.")
|
||||
fin
|
||||
pause(800)
|
||||
return NULL
|
||||
fin
|
||||
pMod = pMod=>p_nextObj
|
||||
loop
|
||||
return NULL
|
||||
fin
|
||||
return item // general 'use' handled by outer engine, because it might involve graphics
|
||||
end
|
||||
@ -435,7 +571,7 @@ end
|
||||
// Select an item and drop it. Returns TRUE if anything changed
|
||||
def doDestroy(player, item)#1
|
||||
clearMenuRect()
|
||||
rawDisplayStr("Destroy ")
|
||||
rawDisplayStr("Drop ")
|
||||
_displayItemName(item)
|
||||
rawDisplayStr(" (Y/N)?")
|
||||
if getYN()
|
||||
@ -451,11 +587,9 @@ def matchEquipped(player, match)#1
|
||||
word item
|
||||
item = player=>p_items
|
||||
while item
|
||||
if (item->t_type == match->t_type) and (item->t_type == TYPE_WEAPON or item->t_type == TYPE_ARMOR)
|
||||
if item <> match and (item->b_flags & ITEM_FLAG_EQUIP)
|
||||
if item->t_type <> TYPE_ARMOR or (item=>s_itemKind == match=>s_itemKind)
|
||||
return item
|
||||
fin
|
||||
if item->t_type == match->t_type and item <> match and isEquipped(item)
|
||||
if item->t_type <> TYPE_ARMOR or (item=>s_itemKind == match=>s_itemKind)
|
||||
return item
|
||||
fin
|
||||
fin
|
||||
item = item=>p_nextObj
|
||||
@ -466,7 +600,7 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayItems(pItem1, pItem2)#0
|
||||
clearMainRect()
|
||||
rawDisplayf1("^V000\n^J^J^L^J^T%DInventory", STATS_COL_1)
|
||||
rawDisplayf1("^T%DInventory", STATS_COL_1)
|
||||
if pItem2
|
||||
rawDisplayf1("^T%DEquipped", STATS_COL_2)
|
||||
fin
|
||||
@ -477,37 +611,69 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def interactWithItem(player, item)#1
|
||||
word comp, quantity
|
||||
byte sel
|
||||
byte sel, ok
|
||||
ok = TRUE
|
||||
displayItems(item, matchEquipped(player, item))
|
||||
while TRUE
|
||||
displayItems(item, matchEquipped(player, item))
|
||||
showItemMenu(item)
|
||||
if ok
|
||||
showItemMenu(item)
|
||||
else
|
||||
beep
|
||||
fin
|
||||
ok = FALSE
|
||||
sel = getUpperKey()
|
||||
rawDisplayf1(" %c\n", sel)
|
||||
when sel
|
||||
// Equip/unequip player with weapon/armor
|
||||
// Equip player with weapon/armor
|
||||
is 'E'
|
||||
if item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON
|
||||
if (item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON) and !isEquipped(item)
|
||||
doEquip(player, item)
|
||||
return NULL
|
||||
else
|
||||
beep
|
||||
return displayDone()
|
||||
fin
|
||||
break
|
||||
// Use an item
|
||||
// Use or Unequip an item
|
||||
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
|
||||
else
|
||||
beep
|
||||
elsif (item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON) and isEquipped(item)
|
||||
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
|
||||
break
|
||||
// Destroy an item
|
||||
is 'D'
|
||||
if doDestroy(player, item); return NULL; fin
|
||||
if !isEquipped(item)
|
||||
if doDestroy(player, item)
|
||||
displayDone()
|
||||
return NULL
|
||||
fin
|
||||
ok = TRUE
|
||||
fin
|
||||
break
|
||||
is $1B // Esc
|
||||
return NULL
|
||||
otherwise beep
|
||||
wend
|
||||
loop
|
||||
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)
|
||||
redisplay = 1
|
||||
totalItems = countList(player=>p_items)
|
||||
elsif redisplay > 0
|
||||
clearInvRect()
|
||||
fin
|
||||
if redisplay > 0
|
||||
if mode == 'I'
|
||||
itemsOnPage = showInventory(player, i_page, 0)
|
||||
showDerived(player)
|
||||
else // 'S'
|
||||
showSkills(player)
|
||||
fin
|
||||
redisplay = 0
|
||||
elsif redisplay > 0
|
||||
clearInvRect()
|
||||
fin
|
||||
if redisplay > 0 and mode == 'I'
|
||||
itemsOnPage = showInventory(player, i_page, 0)
|
||||
fin
|
||||
redisplay = 0
|
||||
|
||||
if !noRepeatMenu
|
||||
if mode == 'I'
|
||||
@ -563,22 +729,24 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
|
||||
// Get a key, do something
|
||||
sel = getUpperKey()
|
||||
when sel
|
||||
is '1'
|
||||
is '2'
|
||||
is '3'
|
||||
is '1'; is '2'; is '3'
|
||||
sel = sel - '1'
|
||||
if player_num <> sel and countList(global=>p_players) > sel
|
||||
if countList(global=>p_players) > sel
|
||||
player_num = sel
|
||||
i_page = 0
|
||||
redisplay = 2
|
||||
else
|
||||
beep
|
||||
fin
|
||||
break
|
||||
// Next inventory page
|
||||
is '>'
|
||||
if mode=='I' and totalItems > (i_page + 1) * INV_ROWS
|
||||
i_page++
|
||||
redisplay = 1
|
||||
else
|
||||
beep
|
||||
noRepeatMenu = TRUE
|
||||
fin
|
||||
break
|
||||
// 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
|
||||
i_page--
|
||||
redisplay = 1
|
||||
else
|
||||
beep
|
||||
noRepeatMenu = TRUE
|
||||
fin
|
||||
break
|
||||
// Other operations...
|
||||
@ -604,13 +775,16 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
|
||||
break
|
||||
is '%' // add item cheat
|
||||
if global->b_godmode
|
||||
clearMainRect()
|
||||
pGodModule=>godmode_addItem(player)
|
||||
redisplay = 1
|
||||
redisplay = 2
|
||||
fin
|
||||
break
|
||||
is '9' // add player cheat
|
||||
if global->b_godmode
|
||||
clearMainRect()
|
||||
pGodModule=>godmode_addPlayer()
|
||||
redisplay = 2
|
||||
fin
|
||||
break
|
||||
is '+' // level up cheat
|
||||
@ -620,24 +794,30 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
|
||||
fin
|
||||
break
|
||||
is $1B // Esc
|
||||
mmgr(CHECK_MEM, 0)
|
||||
mmgr(HEAP_COLLECT, 0)
|
||||
return NULL
|
||||
otherwise
|
||||
if sel == 'X' and mode <> 'I'
|
||||
if sel == 'X' and mode <> 'I' // switch from stats to inv
|
||||
mode = 'I'
|
||||
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'
|
||||
redisplay = 2
|
||||
elsif mode == 'I'
|
||||
sel = sel - 'A'
|
||||
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 countList(player=>p_items) <= i_page * INV_ROWS // destroyed last item on pg 2
|
||||
i_page--
|
||||
fin
|
||||
redisplay = 2
|
||||
else
|
||||
beep
|
||||
noRepeatMenu = TRUE
|
||||
fin
|
||||
elsif mode == 'S'
|
||||
else // mode == 'S'
|
||||
noRepeatMenu = TRUE
|
||||
if sel >= 'A' and (sel-'A' < nSkills)
|
||||
adjustSkill(player, sel - 'A', 1)
|
||||
@ -647,12 +827,10 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
|
||||
beep
|
||||
noRepeatMenu = FALSE
|
||||
fin
|
||||
else
|
||||
beep
|
||||
fin
|
||||
wend
|
||||
until 0
|
||||
return NULL
|
||||
return NULL // never reached
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -755,9 +933,9 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def formatEquipped(num)#1
|
||||
if num == 1
|
||||
rawDisplayStr("Y")
|
||||
rawDisplayStr("yes")
|
||||
else
|
||||
rawDisplayStr("N")
|
||||
rawDisplayStr("no")
|
||||
fin
|
||||
return 0
|
||||
end
|
||||
@ -818,7 +996,6 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayWeaponStats(pItem1, pItem2)#0
|
||||
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("Clip", pItem1, pItem2, b_clipSize, @byteField, @formatNum)
|
||||
displayTwoCol("Melee", pItem1, pItem2, r_meleeDmg, @wordField, @formatDice)
|
||||
@ -832,25 +1009,19 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayArmorStats(pItem1, pItem2)#0
|
||||
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)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayStuffStats(pItem1, pItem2)#0
|
||||
// Nothing special
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def _displayItemStats(pItem1, pItem2)#1
|
||||
word pMod1, pMod2
|
||||
|
||||
// First, show the item type and name
|
||||
when pItem1->t_type
|
||||
is TYPE_ITEM; rawDisplayStr("\nItem"); break
|
||||
is TYPE_WEAPON; rawDisplayStr("\nWeapon"); break
|
||||
is TYPE_ARMOR; rawDisplayStr("\nArmor"); break
|
||||
is TYPE_STUFF; rawDisplayStr("\nSupply"); break
|
||||
is TYPE_PLAIN_ITEM
|
||||
is TYPE_FANCY_ITEM; rawDisplayStr("\nItem"); break
|
||||
is TYPE_WEAPON; rawDisplayStr("\nWeapon"); break
|
||||
is TYPE_ARMOR; rawDisplayStr("\nArmor"); break
|
||||
otherwise fatal("tItem")
|
||||
wend
|
||||
tabTo(STATS_COL_1); _displayItemName(pItem1)
|
||||
@ -862,25 +1033,28 @@ def _displayItemStats(pItem1, pItem2)#1
|
||||
when pItem1->t_type
|
||||
is TYPE_WEAPON; displayWeaponStats(pItem1, pItem2); break
|
||||
is TYPE_ARMOR; displayArmorStats(pItem1, pItem2); break
|
||||
is TYPE_STUFF; displayStuffStats(pItem1, pItem2); break
|
||||
wend
|
||||
|
||||
// If either item has modifiers, show them
|
||||
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
|
||||
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
|
||||
if pMod1 or pMod2
|
||||
rawDisplayStr("\nSpecial")
|
||||
while pMod1 or pMod2
|
||||
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
|
||||
fin
|
||||
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
|
||||
fin
|
||||
loop
|
||||
@ -892,13 +1066,14 @@ end
|
||||
// For non-countable items, display singular name.
|
||||
// For countable "stuff" (e.g. ammo), display the count and appropriate singular or plural name.
|
||||
def _displayItemName(pItem)#1
|
||||
if pItem->t_type == TYPE_STUFF
|
||||
setPlural(pItem=>w_count <> 1)
|
||||
isPlural = FALSE
|
||||
if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
|
||||
isPlural = TRUE
|
||||
rawDisplayf1("%d ", pItem=>w_count)
|
||||
else
|
||||
setPlural(FALSE)
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -9,19 +9,17 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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_Player[] = Player, p_nextObj, s_name, p_combatNext, p_skills, p_items, p_effects, 0
|
||||
byte typeTbl_Modifier[] = Modifier, p_nextObj, s_name, 0
|
||||
byte typeTbl_Effect[] = Effect, p_nextObj, s_name, 0
|
||||
byte typeTbl_Item[] = Item, 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_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_EnemyGroup[] = EnemyGroup, p_nextObj, p_enemies, 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, 0
|
||||
byte typeTbl_Modifier[] = Modifier, p_nextObj, s_name, 0
|
||||
byte typeTbl_PlainItem[] = PlainItem, p_nextObj, s_name, 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_Armor[] = Armor, p_nextObj, s_name, s_itemKind, p_modifiers, 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_EncounterZone[] = EncounterZone, p_nextObj, s_name, 0
|
||||
|
||||
word typeTbls = @typeTbl_Global, @typeTbl_Player, @typeTbl_Modifier, @typeTbl_Effect, @typeTbl_Item
|
||||
word = @typeTbl_Weapon, @typeTbl_Armor, @typeTbl_Stuff, @typeTbl_Enemy, @typeTbl_EnemyGroup
|
||||
word = @typeTbl_EncounterZone
|
||||
word typeTbls = @typeTbl_Global, @typeTbl_Player, @typeTbl_Modifier, @typeTbl_PlainItem, @typeTbl_FancyItem
|
||||
word = @typeTbl_Weapon, @typeTbl_Armor, @typeTbl_Enemy, @typeTbl_EnemyGroup, @typeTbl_EncounterZone
|
||||
word = 0
|
||||
|
@ -36,8 +36,7 @@ struc Global
|
||||
word w_heapSize
|
||||
word w_typeHash
|
||||
|
||||
// General flags maintained by scripts. Linked list of Modifiers.
|
||||
word p_gameFlags
|
||||
byte[32] ba_gameFlags // General flags maintained by scripts (array of 256 bits = 32 bytes)
|
||||
byte b_curAvatar
|
||||
|
||||
// god mode flag
|
||||
@ -86,7 +85,6 @@ struc Player
|
||||
// Lists
|
||||
word p_skills // list:Modifier
|
||||
word p_items // list:Item
|
||||
word p_effects // list:Effect
|
||||
end
|
||||
|
||||
// Combat skills, weapon modifiers, etc.
|
||||
@ -98,27 +96,29 @@ struc Modifier
|
||||
word w_modValue
|
||||
end
|
||||
|
||||
// Buffs and debuffs, that last until a specified time
|
||||
const TYPE_EFFECT = $83
|
||||
struc Effect
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
byte s_name
|
||||
word w_modValue
|
||||
word w_endTurn
|
||||
end
|
||||
|
||||
const TYPE_ITEM = $84
|
||||
struc Item
|
||||
// Plain items are for things that don't stack or have stat effects,
|
||||
// but may have meaning to scenario logic (e.g. keys, tokens, etc.)
|
||||
const TYPE_PLAIN_ITEM = $83
|
||||
struc PlainItem
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
word s_name
|
||||
word s_itemKind
|
||||
word w_price
|
||||
word p_modifiers // list:modifier
|
||||
// Usables properties
|
||||
byte b_maxUses
|
||||
byte b_curUses
|
||||
end
|
||||
|
||||
const TYPE_FANCY_ITEM = $84
|
||||
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
|
||||
|
||||
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
|
||||
struc Weapon
|
||||
// Item properties
|
||||
// Plain item properties
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
word s_name
|
||||
word s_itemKind
|
||||
word w_price
|
||||
word p_modifiers // list:modifier
|
||||
// Usables properties
|
||||
byte b_maxUses
|
||||
byte b_curUses
|
||||
// Weapon properties
|
||||
byte b_flags // WEAPON_FLAG_* above
|
||||
word s_ammoKind
|
||||
word s_itemKind // for skill matching
|
||||
word p_modifiers // list:modifier
|
||||
byte b_flags // WEAPON_FLAG_* above
|
||||
word s_ammoKind // for matching to Stackable ammo
|
||||
byte b_clipSize
|
||||
byte b_clipCurrent
|
||||
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
|
||||
struc Armor
|
||||
// General item properties
|
||||
// Plain item properties
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
word s_name
|
||||
word s_itemKind
|
||||
word w_price
|
||||
word p_modifiers // list:modifier
|
||||
// Usables properties
|
||||
byte b_maxUses
|
||||
byte b_curUses
|
||||
// Armor properties
|
||||
word s_itemKind
|
||||
word p_modifiers // list:modifier
|
||||
byte b_flags // ARMOR_FLAG_* above
|
||||
byte b_armorValue
|
||||
end
|
||||
|
||||
// Countable things, e.g. ammo and pelts
|
||||
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
|
||||
const TYPE_ENEMY = $87
|
||||
struc Enemy
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
@ -204,7 +183,7 @@ struc Enemy
|
||||
word r_goldLoot // monetary loot when killed, as 3 hex digits for dice
|
||||
end
|
||||
|
||||
const TYPE_ENEMY_GROUP = $89
|
||||
const TYPE_ENEMY_GROUP = $88
|
||||
struc EnemyGroup
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
@ -212,7 +191,7 @@ struc EnemyGroup
|
||||
byte b_enemyGroupRange
|
||||
end
|
||||
|
||||
const TYPE_ENCOUNTER_ZONE = $8A
|
||||
const TYPE_ENCOUNTER_ZONE = $89
|
||||
struc EncounterZone
|
||||
byte t_type
|
||||
word p_nextObj
|
||||
|
@ -31,8 +31,8 @@ word pItemsModule, pPartyModule
|
||||
const MAX_PAGE_ITEMS = 20 // should be plenty
|
||||
word pageItems[MAX_PAGE_ITEMS]
|
||||
word pagePrices[MAX_PAGE_ITEMS]
|
||||
word pagePlayers[MAX_PAGE_ITEMS]
|
||||
word pMatchPlayer
|
||||
byte playerNum, playerCount
|
||||
word pPlayer
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Definitions used by assembly code
|
||||
@ -61,14 +61,14 @@ def unloadExtraModules()#0
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayBuyTitle(pageNum, nPages)#0
|
||||
def displayTitle(titleAction, columnAction, pageNum, nPages)#0
|
||||
clearWindow()
|
||||
// Can't use centering mode on oversize window - font engine can't handle width > 255
|
||||
rawDisplayStr("^Y^I Buying")
|
||||
rawDisplayf2("^I %s %s ", pPlayer=>s_name, titleAction)
|
||||
if (nPages > 1)
|
||||
rawDisplayf2(" - p. %d/%d", pageNum+1, nPages)
|
||||
rawDisplayf2("p.%d/%d ", pageNum+1, nPages)
|
||||
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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -77,19 +77,45 @@ def displayItemLine(num)#0
|
||||
pPartyModule()=>party_displayItemName(pageItems[num])
|
||||
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
|
||||
byte itemNum
|
||||
word pFunc, pItem
|
||||
|
||||
displayBuyTitle(pageNum, nPages)
|
||||
// Clear stuff from previous page
|
||||
mmgr(CHECK_MEM, 0)
|
||||
mmgr(HEAP_COLLECT, 0)
|
||||
|
||||
displayTitle("buying", "Browse", pageNum, nPages)
|
||||
pFunc = pItemTbl + ((pageNum*PAGE_SIZE) << 1)
|
||||
for itemNum = 0 to PAGE_SIZE-1
|
||||
if !(*pFunc); break; fin
|
||||
pItem = (*pFunc)()
|
||||
if pItem->t_type == TYPE_STUFF
|
||||
if pItem->t_type == TYPE_FANCY_ITEM
|
||||
pItem=>w_count = pItem=>w_storeAmount
|
||||
fin
|
||||
pageItems[itemNum] = pItem
|
||||
@ -97,14 +123,7 @@ def displayBuyPage(pItemTbl, markupRatio, pageNum, nPages)#1
|
||||
displayItemLine(itemNum)
|
||||
pFunc = pFunc + 2
|
||||
next
|
||||
|
||||
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].")
|
||||
displayPaging("Browse", itemNum, pageNum, nPages)
|
||||
return itemNum
|
||||
end
|
||||
|
||||
@ -113,7 +132,7 @@ def displayItemBrowse(pItem1, price, pItem2)#0
|
||||
clearWindow()
|
||||
rawDisplayf1("^T108^I Browse ^N\n\n^T%D^LMerchandise^L", STATS_COL_1)
|
||||
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
|
||||
pPartyModule()=>party_displayItemStats(pItem1, pItem2)
|
||||
rawDisplayf2("\n\nPrice^T%D%d", STATS_COL_1, price)
|
||||
@ -122,26 +141,20 @@ end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def matchEquipped(pMatch, nSkip)#1
|
||||
word pPlayer, pItem
|
||||
pPlayer = global=>p_players
|
||||
pMatchPlayer = pPlayer
|
||||
while pPlayer
|
||||
pItem = pPlayer=>p_items
|
||||
while pItem
|
||||
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->t_type <> TYPE_ARMOR or (pItem=>s_itemKind == pMatch=>s_itemKind)
|
||||
if nSkip == 0
|
||||
pMatchPlayer = pPlayer
|
||||
return pItem
|
||||
fin
|
||||
nSkip = nSkip - 1
|
||||
word pItem
|
||||
pItem = pPlayer=>p_items
|
||||
while pItem
|
||||
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->t_type <> TYPE_ARMOR or (pItem=>s_itemKind == pMatch=>s_itemKind)
|
||||
if nSkip == 0
|
||||
return pItem
|
||||
fin
|
||||
nSkip = nSkip - 1
|
||||
fin
|
||||
fin
|
||||
pItem = pItem=>p_nextObj
|
||||
loop
|
||||
pPlayer = pPlayer=>p_nextObj
|
||||
fin
|
||||
pItem = pItem=>p_nextObj
|
||||
loop
|
||||
return NULL
|
||||
end
|
||||
@ -169,6 +182,12 @@ def askQuantity(nMax)#1
|
||||
return num
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displayMsg(msg, beep)#0
|
||||
rawDisplayf1("^T000^C%s", msg)
|
||||
pause(800)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def browseItem(num)#0
|
||||
word pItem, price, compSkip, pComp, quantity
|
||||
@ -180,24 +199,22 @@ def browseItem(num)#0
|
||||
displayItemBrowse(pItem, price, matchEquipped(pItem, compSkip))
|
||||
displayItemMenu(price, compSkip or matchEquipped(pItem, 1+compSkip))
|
||||
sel = getUpperKey()
|
||||
rawDisplayf1(" %c\n", sel)
|
||||
if sel == 'B' and price <= global=>w_gold
|
||||
matchEquipped(pItem, compSkip) // to set pMatchPlayer
|
||||
pComp = scanForNamedObj(pMatchPlayer=>p_items, pItem=>s_name)
|
||||
pComp = scanForNamedObj(pPlayer=>p_items, pItem=>s_name)
|
||||
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)
|
||||
else
|
||||
rawDisplayStr("\nDuplicate item.")
|
||||
rawDisplayStr("^T000^CDuplicate item.")
|
||||
beep()
|
||||
pause(1000)
|
||||
pause(400)
|
||||
continue
|
||||
fin
|
||||
else
|
||||
addToList(@pMatchPlayer=>p_items, pItem)
|
||||
addToList(@pPlayer=>p_items, pItem)
|
||||
fin
|
||||
global=>w_gold = global=>w_gold - price
|
||||
rawDisplayStr("\nPurchase complete.")
|
||||
rawDisplayStr("^T000^CDone.")
|
||||
pause(800)
|
||||
break
|
||||
elsif sel == 'N' and (compSkip or matchEquipped(pItem, 1+compSkip))
|
||||
@ -211,16 +228,24 @@ def browseItem(num)#0
|
||||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def storeSetup()#0
|
||||
loadExtraModules()
|
||||
setBigWindow()
|
||||
|
||||
playerCount = countList(global=>p_players)
|
||||
playerNum = 0
|
||||
pPlayer = numToPlayer(playerNum)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def _buyFromStore(storeCode, profitPercent)#1
|
||||
word pItemTbl, choice, ratio
|
||||
byte nItemsOnPage, pageNum, nPages, redisplay
|
||||
|
||||
loadExtraModules()
|
||||
storeSetup()
|
||||
pItemTbl = pItemsModule()=>items_forStoreCode(storeCode)
|
||||
|
||||
setBigWindow()
|
||||
|
||||
nPages = (countArray(pItemTbl) + PAGE_SIZE - 1) / PAGE_SIZE
|
||||
pageNum = 0
|
||||
|
||||
@ -233,8 +258,13 @@ def _buyFromStore(storeCode, profitPercent)#1
|
||||
fin
|
||||
choice = getUpperKey()
|
||||
redisplay = TRUE
|
||||
if choice >= '1' and (choice-'1') < nPages
|
||||
pageNum = choice - '1'
|
||||
if choice == '<' and pageNum
|
||||
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)
|
||||
browseItem(choice-'A')
|
||||
elsif choice == $1B // Esc
|
||||
@ -246,53 +276,38 @@ def _buyFromStore(storeCode, profitPercent)#1
|
||||
loop
|
||||
|
||||
unloadExtraModules()
|
||||
mmgr(CHECK_MEM, 0)
|
||||
return mmgr(HEAP_COLLECT, 0)
|
||||
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
|
||||
word pPlayer, pItem, itemsOnPage, totalItems, price
|
||||
word pItem, itemsOnPage, totalItems, price
|
||||
byte ok
|
||||
itemsOnPage = 0
|
||||
totalItems = 0
|
||||
pPlayer = global=>p_players
|
||||
while pPlayer
|
||||
pItem = pPlayer=>p_items
|
||||
while pItem
|
||||
ok = pItem=>w_price > 0
|
||||
if pItem->t_type == TYPE_STUFF
|
||||
ok = FALSE // too much trouble to figure out stuff prices
|
||||
elsif pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR
|
||||
if pItem->b_flags & ITEM_FLAG_EQUIP; ok = FALSE; fin
|
||||
pItem = pPlayer=>p_items
|
||||
while pItem
|
||||
ok = pItem=>w_price > 0
|
||||
if pItem->t_type == TYPE_FANCY_ITEM
|
||||
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
|
||||
if pItem->b_flags & ITEM_FLAG_EQUIP; ok = FALSE; fin // don't sell equipped things
|
||||
fin
|
||||
if ok
|
||||
price = max(0, pItem=>w_price - addRatio(pItem=>w_price, markdownRatio))
|
||||
if !price; ok = FALSE; fin
|
||||
fin
|
||||
if ok
|
||||
if totalItems >= skipItems and itemsOnPage < PAGE_SIZE
|
||||
pageItems[itemsOnPage] = pItem
|
||||
pagePrices[itemsOnPage] = price
|
||||
displayItemLine(itemsOnPage)
|
||||
itemsOnPage++
|
||||
fin
|
||||
if ok
|
||||
price = max(0, pItem=>w_price - addRatio(pItem=>w_price, markdownRatio))
|
||||
if !price; ok = FALSE; fin
|
||||
fin
|
||||
if ok
|
||||
if totalItems >= skipItems and itemsOnPage < PAGE_SIZE
|
||||
pageItems[itemsOnPage] = pItem
|
||||
pagePrices[itemsOnPage] = price
|
||||
pagePlayers[itemsOnPage] = pPlayer
|
||||
displayItemLine(itemsOnPage)
|
||||
itemsOnPage++
|
||||
fin
|
||||
totalItems++
|
||||
fin
|
||||
pItem = pItem=>p_nextObj
|
||||
loop
|
||||
pPlayer = pPlayer=>p_nextObj
|
||||
totalItems++
|
||||
fin
|
||||
pItem = pItem=>p_nextObj
|
||||
loop
|
||||
if skipItems == 9999; return totalItems; fin
|
||||
return itemsOnPage
|
||||
@ -301,18 +316,12 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def displaySellPage(markdownRatio, pageNum, nPages)#1
|
||||
word nItems
|
||||
displaySellTitle(pageNum, nPages)
|
||||
displayTitle("selling", "Sell", pageNum, nPages)
|
||||
nItems = iterateSellables(pageNum * PAGE_SIZE, markdownRatio)
|
||||
if !nItems
|
||||
rawDisplayStr("\n\nNothing to sell here.")
|
||||
fin
|
||||
rawDisplayf1("\n^V166Gold: %d. Sell [A", global=>w_gold)
|
||||
if nItems > 1; rawDisplayf1("-%c", nItems-1+'A'); fin
|
||||
rawDisplayStr("], ")
|
||||
if nPages > 1
|
||||
rawDisplayf1("p. [1-%d], ", nPages)
|
||||
fin
|
||||
rawDisplayStr("or [Esc].")
|
||||
displayPaging("Sell", nItems, pageNum, nPages)
|
||||
return nItems
|
||||
end
|
||||
|
||||
@ -323,12 +332,10 @@ def sellItem(num)#0
|
||||
pItem = pageItems[num]
|
||||
price = pagePrices[num]
|
||||
|
||||
clearWindow()
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
@ -337,27 +344,34 @@ def _sellToStore(profitPercent)#1
|
||||
word pItemTbl, choice, ratio
|
||||
byte nItemsOnPage, pageNum, totalItems, nPages, redisplay
|
||||
|
||||
loadExtraModules()
|
||||
setBigWindow()
|
||||
|
||||
pageNum = 0
|
||||
storeSetup()
|
||||
|
||||
ratio = percentToRatio(profitPercent) / 2 // half profit on buying, half on selling
|
||||
totalItems = iterateSellables(9999, 0) // initialize count for paging calcs
|
||||
|
||||
redisplay = TRUE
|
||||
while totalItems > 0
|
||||
while TRUE
|
||||
nPages = (totalItems + PAGE_SIZE - 1) / PAGE_SIZE // recalc each time since totalItems changes
|
||||
pageNum = min(nPages-1, pageNum)
|
||||
if redisplay
|
||||
nItemsOnPage = displaySellPage(ratio, pageNum, nPages)
|
||||
fin
|
||||
choice = getUpperKey()
|
||||
redisplay = TRUE
|
||||
if choice >= '1' and (choice-'1') < nPages
|
||||
pageNum = choice - '1'
|
||||
if choice == '<' and pageNum
|
||||
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)
|
||||
sellItem(choice-'A')
|
||||
totalItems = iterateSellables(9999, 0)
|
||||
if totalItems <= pageNum * PAGE_SIZE // sold last item on pg 2
|
||||
pageNum--
|
||||
fin
|
||||
elsif choice == $1B // Esc
|
||||
break
|
||||
else
|
||||
@ -367,6 +381,7 @@ def _sellToStore(profitPercent)#1
|
||||
loop
|
||||
|
||||
unloadExtraModules()
|
||||
mmgr(CHECK_MEM, 0)
|
||||
return mmgr(HEAP_COLLECT, 0)
|
||||
end
|
||||
|
||||
|
@ -1763,6 +1763,8 @@ pl_advance: !zone
|
||||
pha
|
||||
adc walkDirs+1,x
|
||||
sta playerX+1
|
||||
jsr .chk
|
||||
sta .ora+1
|
||||
|
||||
lda playerY
|
||||
pha
|
||||
@ -1773,27 +1775,15 @@ pl_advance: !zone
|
||||
pha
|
||||
adc walkDirs+3,x
|
||||
sta playerY+1
|
||||
|
||||
; Check if the new position is blocked
|
||||
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
|
||||
jsr .chk
|
||||
.ora ora #11 ; self-modified above
|
||||
beq .ok
|
||||
; Blocked! Restore old position.
|
||||
pla
|
||||
sta playerY+1
|
||||
pla
|
||||
sta playerY
|
||||
pla
|
||||
sta playerX+1
|
||||
pla
|
||||
sta playerX
|
||||
ldx #3
|
||||
- pla
|
||||
sta playerX,x
|
||||
dex
|
||||
bpl -
|
||||
ldy #0
|
||||
beq .done
|
||||
.ok ; Not blocked. See if we're in a new map tile.
|
||||
@ -1820,6 +1810,20 @@ pl_advance: !zone
|
||||
.done tya ; retrieve ret value
|
||||
ldy #0 ; hi byte of ret is always 0
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user