mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-06-26 00:29:30 +00:00
Refactored enemies table to make it smaller and split it in half. It was getting too big for memory.
This commit is contained in:
parent
30405be0c2
commit
2b6edd912a
|
@ -126,6 +126,7 @@ class A2PackPartitions
|
|||
def automapPat = null // regexp Pattern to match script names to automap tiles
|
||||
def automapSpecials = []
|
||||
|
||||
def automapExitTile = -1
|
||||
def maxMapSections = 0
|
||||
|
||||
def itemNameToFunc = [:]
|
||||
|
@ -1021,6 +1022,8 @@ class A2PackPartitions
|
|||
if (cat == "automap") {
|
||||
automapTiles[lname] = ++tileNum
|
||||
patStrs << lname
|
||||
if (lname == "exit")
|
||||
automapExitTile = tileNum
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2357,10 +2360,12 @@ class A2PackPartitions
|
|||
compileModule("godmode", "src/plasma/")
|
||||
compileModule("intimate", "src/plasma/")
|
||||
compileModule("automap", "src/plasma/")
|
||||
compileModule("sndseq", "src/plasma/")
|
||||
//compileModule("sndseq", "src/plasma/") // not yet
|
||||
compileModule("questlog", "src/plasma/")
|
||||
lastSysModule = modules.size() // used only for reporting
|
||||
compileModule("gen_enemies", "src/plasma/")
|
||||
compileModule("gen_enemies0", "src/plasma/")
|
||||
compileModule("gen_enemies1", "src/plasma/")
|
||||
compileModule("gen_items", "src/plasma/")
|
||||
compileModule("gen_players", "src/plasma/")
|
||||
compileModule("gen_flags", "src/plasma/")
|
||||
|
@ -2447,6 +2452,10 @@ class A2PackPartitions
|
|||
def scriptDir = "build/src/mapScripts/"
|
||||
if (!new File(scriptDir).exists())
|
||||
new File(scriptDir).mkdirs()
|
||||
new File("build/src/mapScripts/gen_mapSpecials.plh.new").withWriter { out ->
|
||||
out.println("// Generated code - DO NOT MODIFY BY HAND\n")
|
||||
out.println("const automap_exitTileNum = $automapExitTile")
|
||||
}
|
||||
new File("build/src/mapScripts/gen_mapSpecials.s.new").withWriter { out ->
|
||||
out.println("; Generated code - DO NOT MODIFY BY HAND\n")
|
||||
out.println("*=0 ; origin irrelevant")
|
||||
|
@ -2490,6 +2499,7 @@ class A2PackPartitions
|
|||
out.println(" !byte \$FF; end of all maps")
|
||||
}
|
||||
replaceIfDiff("build/src/mapScripts/gen_mapSpecials.s")
|
||||
replaceIfDiff("build/src/mapScripts/gen_mapSpecials.plh")
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2801,9 +2811,10 @@ class A2PackPartitions
|
|||
packAutomapTileSet(dataIn)
|
||||
|
||||
// Play around with music
|
||||
def midiFile = new File(xmlFile.getAbsoluteFile().getParentFile(), "song.mid")
|
||||
if (midiFile.exists())
|
||||
packSong(midiFile)
|
||||
// For now, not using this, and need to save space on disk 1.
|
||||
//def midiFile = new File(xmlFile.getAbsoluteFile().getParentFile(), "song.mid")
|
||||
//if (midiFile.exists())
|
||||
// packSong(midiFile)
|
||||
|
||||
// Divvy up the images by category
|
||||
def titleImgs = []
|
||||
|
@ -2969,16 +2980,17 @@ class A2PackPartitions
|
|||
}
|
||||
else if (ch == ')') {
|
||||
inParen = false
|
||||
inSlash = false
|
||||
ch = 0
|
||||
}
|
||||
else if (ch == '/') {
|
||||
else if (inParen && ch == '/') {
|
||||
inSlash = true
|
||||
ch = 0
|
||||
}
|
||||
else if (!isAlnum(ch))
|
||||
inSlash = false
|
||||
|
||||
if (ch && !inParen && !inSlash) {
|
||||
if (ch && (!inParen || !inSlash)) {
|
||||
if ((ch >= 'A' && ch <= 'Z') || ch == ' ' || ch == '_')
|
||||
needSeparator = (idx > 0)
|
||||
if (isAlnum(ch)) {
|
||||
|
@ -3060,6 +3072,8 @@ class A2PackPartitions
|
|||
{
|
||||
def str = row.@"attack-text"
|
||||
strings[str] = "_SE_${humanNameToSymbol(str, true)}"
|
||||
str = row.@"name"
|
||||
strings[str] = "_SE_${humanNameToSymbol(str, true)}"
|
||||
str = row.@"loot-code"
|
||||
if (str && str != "0")
|
||||
strings[str.toLowerCase()] = "_SE_${humanNameToSymbol(str.toLowerCase(), true)}"
|
||||
|
@ -3070,8 +3084,6 @@ class A2PackPartitions
|
|||
def name = row.@name
|
||||
withContext(name)
|
||||
{
|
||||
out.println("def _NEn_${humanNameToSymbol(name, false)}()")
|
||||
|
||||
def image1 = row.@image1
|
||||
if (!portraits.containsKey(image1))
|
||||
throw new Exception("Image '$image1' not found")
|
||||
|
@ -3099,22 +3111,21 @@ class A2PackPartitions
|
|||
def lootCode = row.@"loot-code" // optional
|
||||
def goldLoot = row.@"gold-loot"; assert goldLoot
|
||||
|
||||
out.println(" return makeEnemy_pt2(makeEnemy_pt1(" +
|
||||
"\"$name\", " +
|
||||
"${parseDice(hitPoints)}, " +
|
||||
"PO${humanNameToSymbol(image1, false)}, " +
|
||||
out.println("word = " +
|
||||
"@${strings[name]}, ${parseDice(hitPoints)} // name, hit dice")
|
||||
out.println("byte = PO${humanNameToSymbol(image1, false)}, " +
|
||||
(image2.size() > 0 ? "PO${humanNameToSymbol(image2, false)}, " : "0, ") +
|
||||
"$attackTypeCode, " +
|
||||
"@${strings[attackText]}, " +
|
||||
"${range.replace("'", "").toInteger()}, " +
|
||||
"${chanceToHit.toInteger()}, " +
|
||||
"${parseDice(damage)}, " +
|
||||
"${parseDice(experience)}), " + // end of pt1
|
||||
"${parseDice(groupSize)}, " +
|
||||
"${lootChance ? lootChance.toInteger() : 10}, " +
|
||||
"${validateLootCode(lootCode, strings)}, " +
|
||||
"${parseDice(goldLoot)})")
|
||||
out.println("end")
|
||||
"$attackTypeCode // img0, img1, attack type")
|
||||
out.println("word = @${strings[attackText]} // attack text")
|
||||
out.println("byte = ${range.replace("'", "").toInteger()}, " +
|
||||
"${chanceToHit.toInteger()} // attack range, chance to hit")
|
||||
out.println("word = ${parseDice(damage)}, " +
|
||||
"${parseDice(experience)}, " +
|
||||
"${parseDice(groupSize)} // damage dice, exp dice, group size dice")
|
||||
out.println("byte = ${lootChance ? lootChance.toInteger() : 10} // loot chance")
|
||||
out.println("word = ${validateLootCode(lootCode, strings)}, " +
|
||||
"${parseDice(goldLoot)} // lootCode, goldLoot")
|
||||
out.println("")
|
||||
|
||||
// Add portrait dependencies based on encounter zone(s)
|
||||
def codesString = row.@"map-code"
|
||||
|
@ -3136,89 +3147,86 @@ class A2PackPartitions
|
|||
|
||||
withContext("enemies sheet")
|
||||
{
|
||||
def columns = sheet.columns.column.collect { it.@name }
|
||||
assert "name" in columns
|
||||
assert "map-code" in columns
|
||||
|
||||
// Header with enemy numbers
|
||||
new File("build/src/plasma/gen_enemies.plh.new").withWriter { out ->
|
||||
out.println("// Generated code - DO NOT MODIFY BY HAND\n")
|
||||
out.println("const enemies_forZone = 0")
|
||||
out.println()
|
||||
sheet.rows.row.eachWithIndex { row, idx ->
|
||||
out.println("const En_${humanNameToSymbol(row."@name", true)} = ${idx+1}")
|
||||
}
|
||||
}
|
||||
replaceIfDiff("build/src/plasma/gen_enemies.plh")
|
||||
|
||||
// Produce the central index file that maps code to enemy numbers
|
||||
new File("build/src/plasma/gen_enemies.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 \"playtype.plh\"")
|
||||
out.println("include \"gen_images.plh\"")
|
||||
out.println("include \"gen_enemies.plh\"")
|
||||
out.println()
|
||||
|
||||
def columns = sheet.columns.column.collect { it.@name }
|
||||
assert "name" in columns
|
||||
assert "map-code" in columns
|
||||
out.println("predef _enemies_forZone(zone)#1")
|
||||
out.println("word[] funcTbl = @_enemies_forZone\n")
|
||||
|
||||
// Pre-define all the enemy creation functions
|
||||
sheet.rows.row.each { row ->
|
||||
out.println("predef _NEn_${humanNameToSymbol(row.@name, false)}")
|
||||
}
|
||||
out.println()
|
||||
|
||||
def strings = [:]
|
||||
sheet.rows.row.each { row ->
|
||||
extractEnemyStrings(row, strings)
|
||||
}
|
||||
strings.each { str, sym ->
|
||||
out.println("byte[] $sym = ${escapeString(str)}")
|
||||
}
|
||||
out.println()
|
||||
|
||||
// Figure out the mapping between "map code" and "enemy", and output the table for that
|
||||
// Figure out the mapping between "map code" and "enemy"
|
||||
def codeToFunc = [:]
|
||||
sheet.rows.row.each { row ->
|
||||
addCodeToFunc("_NEn_${humanNameToSymbol(row.@name, false)}", row.@"map-code", codeToFunc)
|
||||
addCodeToFunc("En_${humanNameToSymbol(row.@name, true)}", row.@"map-code", codeToFunc)
|
||||
}
|
||||
outCodeToFuncTbl("mapCode_", codeToFunc, out)
|
||||
|
||||
// Helper function to fill in the Enemy data structure
|
||||
out.println("""
|
||||
def makeEnemy_pt1(name, hDice, img0, img1, attType, attText, attRange, chanceToHit, dmg, xp)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_ENEMY)
|
||||
p=>s_name = mmgr(HEAP_INTERN, name)
|
||||
p=>w_health = rollDice(hDice) // e.g. 4d6
|
||||
if !img1 or (rand16() % 2)
|
||||
p->b_image = img0
|
||||
else
|
||||
p->b_image = img1
|
||||
fin
|
||||
p->b_attackType = attType
|
||||
p=>s_attackText = mmgr(HEAP_INTERN, attText)
|
||||
p->b_enemyAttackRange = attRange
|
||||
p->b_chanceToHit = chanceToHit
|
||||
p=>r_enemyDmg = dmg
|
||||
p=>r_enemyXP = xp
|
||||
return p
|
||||
end
|
||||
def makeEnemy_pt2(p, groupSize, lootChance, lootCode, goldLoot)
|
||||
p=>r_groupSize = groupSize
|
||||
p->b_lootChance = lootChance
|
||||
if lootCode; p=>s_lootCode = mmgr(HEAP_INTERN, lootCode); fin
|
||||
p=>r_goldLoot = goldLoot
|
||||
return p
|
||||
end
|
||||
""")
|
||||
|
||||
// Now output a function for each enemy
|
||||
sheet.rows.row.each { row ->
|
||||
genEnemy(out, row, strings)
|
||||
}
|
||||
out.println()
|
||||
// Output the code to enemy table
|
||||
outCodeToConstTbl("mapCode_", codeToFunc, out)
|
||||
|
||||
// And finally, a function to select an enemy given a map code.
|
||||
outCodeToFuncMethods("_enemies_forZone", "mapCode_", codeToFunc, out, strings)
|
||||
outCodeToFuncMethods("_enemies_forZone", "mapCode_", codeToFunc, out, null)
|
||||
|
||||
out.println("return @funcTbl")
|
||||
out.println("done")
|
||||
}
|
||||
replaceIfDiff("build/src/plasma/gen_enemies.pla")
|
||||
|
||||
// And generate each subset of the enemy data tables.
|
||||
[0,1].each { subset ->
|
||||
new File("build/src/plasma/gen_enemies${subset}.pla.new").withWriter { out ->
|
||||
out.println("// Generated code - DO NOT MODIFY BY HAND")
|
||||
out.println()
|
||||
out.println("include \"globalDefs.plh\"")
|
||||
out.println("include \"gen_images.plh\"")
|
||||
out.println()
|
||||
|
||||
def strings = [:]
|
||||
sheet.rows.row.eachWithIndex { row, idx ->
|
||||
if ((idx % 2) == subset)
|
||||
extractEnemyStrings(row, strings)
|
||||
}
|
||||
strings.sort().each { str, sym ->
|
||||
out.println("byte[] $sym = ${escapeString(str)}")
|
||||
}
|
||||
out.println()
|
||||
|
||||
// The number of enemies in this subset
|
||||
out.println("byte enemyTblCt = ${(sheet.rows.row.size() + (1-subset)) >> 1} // number of entries")
|
||||
out.println("")
|
||||
|
||||
// Now output the data for each enemy
|
||||
sheet.rows.row.eachWithIndex { row, idx ->
|
||||
if ((idx % 2) == subset)
|
||||
genEnemy(out, row, strings)
|
||||
}
|
||||
out.println()
|
||||
|
||||
// And finally, return a pointer to the table
|
||||
out.println("return @enemyTblCt")
|
||||
out.println("done")
|
||||
}
|
||||
replaceIfDiff("build/src/plasma/gen_enemies${subset}.pla")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3605,6 +3613,18 @@ end
|
|||
}
|
||||
}
|
||||
|
||||
def outCodeToConstTbl(prefix, codeToFunc, out)
|
||||
{
|
||||
codeToFunc.sort { ent -> ent.key.toLowerCase() }.each { code, consts ->
|
||||
consts.eachWithIndex { constVal, index ->
|
||||
out.println(
|
||||
"${index==0 ? "word[] $prefix${humanNameToSymbol(code, false)} = " : "word = "}$constVal")
|
||||
}
|
||||
out.println("word = 0")
|
||||
out.println()
|
||||
}
|
||||
}
|
||||
|
||||
def outCodeToFuncMethods(funcName, prefix, codeToFunc, out, strings = null)
|
||||
{
|
||||
out.println("def $funcName(code)#1")
|
||||
|
|
|
@ -17,6 +17,23 @@ include "gen_enemies.plh"
|
|||
include "gen_modules.plh"
|
||||
include "combat.plh"
|
||||
|
||||
struc EnemyData
|
||||
word s_en_name
|
||||
word r_en_hitDice
|
||||
byte b_en_img0
|
||||
byte b_en_img1
|
||||
byte b_en_attType
|
||||
word s_en_attText
|
||||
byte b_en_attRange
|
||||
byte b_en_chanceToHit
|
||||
word r_en_dmgDice
|
||||
word r_en_expDice
|
||||
word r_en_groupSize
|
||||
byte b_en_lootChance
|
||||
word s_en_lootCode
|
||||
word r_en_goldLoot
|
||||
end
|
||||
|
||||
predef _combat_zoneEncounter(s_encZone)#1
|
||||
word[] funcTbl = @_combat_zoneEncounter
|
||||
|
||||
|
@ -849,10 +866,33 @@ def determineCombatOrder()#0
|
|||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def makeEnemyGroup(enemyFunc)#1
|
||||
def makeEnemy(pData)
|
||||
word p; p = mmgr(HEAP_ALLOC, TYPE_ENEMY)
|
||||
p=>s_name = mmgr(HEAP_INTERN, pData=>s_en_name)
|
||||
p=>w_health = rollDice(pData=>r_en_hitDice) // e.g. 4d6
|
||||
if !pData->b_en_img1 or (rand16() % 2)
|
||||
p->b_image = pData->b_en_img0
|
||||
else
|
||||
p->b_image = pData->b_en_img1
|
||||
fin
|
||||
p->b_attackType = pData->b_en_attType
|
||||
p=>s_attackText = mmgr(HEAP_INTERN, pData=>s_en_attText)
|
||||
p->b_enemyAttackRange = pData->b_en_attRange
|
||||
p->b_chanceToHit = pData->b_en_chanceToHit
|
||||
p=>r_enemyDmg = pData=>r_en_dmgDice
|
||||
p=>r_enemyXP = pData=>r_en_expDice
|
||||
p=>r_groupSize = pData=>r_en_groupSize
|
||||
p->b_lootChance = pData->b_en_lootChance
|
||||
if pData=>s_en_lootCode; p=>s_lootCode = mmgr(HEAP_INTERN, pData=>s_en_lootCode); fin
|
||||
p=>r_goldLoot = pData=>r_en_goldLoot
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def makeEnemyGroup(pEnemyData)#1
|
||||
word p, enem, groupSize
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ENEMY_GROUP)
|
||||
enem = enemyFunc()
|
||||
enem = makeEnemy(pEnemyData)
|
||||
p->b_enemyGroupRange = enem->b_enemyAttackRange
|
||||
if enem=>r_groupSize == 0 // handle unique enemies
|
||||
groupSize = 1
|
||||
|
@ -861,7 +901,7 @@ def makeEnemyGroup(enemyFunc)#1
|
|||
fin
|
||||
addToList(p + p_enemies, enem)
|
||||
while groupSize > 1
|
||||
addToList(p + p_enemies, enemyFunc())
|
||||
addToList(p + p_enemies, makeEnemy(pEnemyData))
|
||||
groupSize--
|
||||
loop
|
||||
|
||||
|
@ -882,14 +922,18 @@ end
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def makeRandomGroup(mapCode)#0
|
||||
word enemiesModule
|
||||
word enemyFunc
|
||||
byte enemyNum
|
||||
word pEnemyData
|
||||
|
||||
enemiesModule = mmgr(QUEUE_LOAD, MOD_GEN_ENEMIES<<8 | RES_TYPE_MODULE)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
enemyNum = randomFromArray(enemiesModule()=>enemies_forZone(mapCode))
|
||||
mmgr(FREE_MEMORY, enemiesModule)
|
||||
|
||||
enemyFunc = randomFromArray(enemiesModule()=>enemies_forZone(mapCode))
|
||||
addToList(@global=>p_enemyGroups, makeEnemyGroup(enemyFunc))
|
||||
|
||||
enemiesModule = mmgr(QUEUE_LOAD, (MOD_GEN_ENEMIES + 1 + ((enemyNum-1)&1))<<8 | RES_TYPE_MODULE)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
pEnemyData = enemiesModule() + 1 + (((enemyNum-1)>>1) * EnemyData)
|
||||
addToList(@global=>p_enemyGroups, makeEnemyGroup(pEnemyData))
|
||||
mmgr(FREE_MEMORY, enemiesModule)
|
||||
end
|
||||
|
||||
|
|
|
@ -400,22 +400,22 @@ def getTextKey()#1
|
|||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def soundKey()#1
|
||||
byte key
|
||||
word engine, funcTbl
|
||||
key = getTextKey
|
||||
if key <> 'S'; return key; fin
|
||||
mmgr(START_LOAD, 1) // code is in partition 1
|
||||
engine = mmgr(QUEUE_LOAD, MOD_SNDSEQ<<8 | RES_TYPE_MODULE)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
funcTbl = engine()
|
||||
^$c053
|
||||
key = funcTbl=>sndseq_play(1) // temporarily, only 1 song
|
||||
^$c052
|
||||
textHome()
|
||||
^$25 = 20
|
||||
return key
|
||||
end
|
||||
//def soundKey()#1
|
||||
// byte key
|
||||
// word engine, funcTbl
|
||||
// key = getTextKey
|
||||
// if key <> 'S'; return key; fin
|
||||
// mmgr(START_LOAD, 1) // code is in partition 1
|
||||
// engine = mmgr(QUEUE_LOAD, MOD_SNDSEQ<<8 | RES_TYPE_MODULE)
|
||||
// mmgr(FINISH_LOAD, 0)
|
||||
// funcTbl = engine()
|
||||
// ^$c053
|
||||
// key = funcTbl=>sndseq_play(1) // temporarily, only 1 song
|
||||
// ^$c052
|
||||
// textHome()
|
||||
// ^$25 = 20
|
||||
// return key
|
||||
//end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def pressAnyKey()#0
|
||||
|
@ -573,7 +573,7 @@ def _newOrLoadGame(ask)#1
|
|||
puts("\n Game: N)ew, ")
|
||||
if existing; puts("L)oad, "); fin
|
||||
puts("I)mport?")
|
||||
key = soundKey() //getTextKey()
|
||||
key = getTextKey() // soundKey()
|
||||
when key
|
||||
is 'N'
|
||||
newGame(); return 1
|
||||
|
|
|
@ -886,12 +886,6 @@ export asm auxMmgr(cmd, wordParam)#1
|
|||
rts
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Jump straight to the system monitor
|
||||
asm goMon()#0
|
||||
jmp $FF69
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Execute a monitor breakpoint
|
||||
export asm brk()#0
|
||||
|
@ -1680,7 +1674,7 @@ end
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Look up the partition for a resource.
|
||||
// sectioNum: 1=map2d, 2=map3d, 3=portrait
|
||||
// sectioNum: 0=version num (do not use here), 1=map2d, 2=map3d, 3=portrait
|
||||
export def lookupResourcePart(sectionNum, resourceNum)#1
|
||||
word ptr
|
||||
byte n, i
|
||||
|
@ -1703,7 +1697,7 @@ export def lookupResourcePart(sectionNum, resourceNum)#1
|
|||
fin
|
||||
|
||||
// Otherwise return the first disk it's on
|
||||
for i = 0 to 8
|
||||
for i = 0 to 7
|
||||
if n & (1<<i); return i+1; fin
|
||||
next
|
||||
fatal("lkupFail2")
|
||||
|
|
Loading…
Reference in New Issue
Block a user