mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-06-27 07:29:32 +00:00
Multiple loot groups per enemy now fully implemented.
This commit is contained in:
parent
562a1bf285
commit
dd9690ad82
|
@ -132,6 +132,7 @@ class A2PackPartitions
|
|||
def automapSpecials = []
|
||||
def stories = [:] // story text to story.num, story.text
|
||||
def storyPartition = 0
|
||||
def lootCodes = [:] // loot code to loot.num
|
||||
|
||||
def automapExitTile = -1
|
||||
def maxMapSections = 0
|
||||
|
@ -139,7 +140,6 @@ class A2PackPartitions
|
|||
def itemNameToFunc = [:]
|
||||
def playerNameToFunc = [:]
|
||||
|
||||
def allLootCodes
|
||||
def nWeapons
|
||||
def nArmors
|
||||
|
||||
|
@ -3118,7 +3118,10 @@ class A2PackPartitions
|
|||
// significant, calculate a digest and replace the excess characters
|
||||
// with part of it.
|
||||
def bigHash = Integer.toString(result.hashCode(), 36)
|
||||
result = result.substring(0, 10) + "_" + bigHash.substring(bigHash.length() - 4)
|
||||
def toAdd = bigHash.substring(bigHash.length() - 4)
|
||||
if (allUpper)
|
||||
toAdd = toAdd.toUpperCase()
|
||||
result = result.substring(0, 10) + "_" + toAdd
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -3159,18 +3162,15 @@ class A2PackPartitions
|
|||
return String.format("\$%X", ((nDice << 12) | (dieSize << 8) | add))
|
||||
}
|
||||
|
||||
def validateLootCode(code, strings = null)
|
||||
def validateLootCode(code)
|
||||
{
|
||||
if (!code || code == "0")
|
||||
return "NULL"
|
||||
if (!allLootCodes.contains(code.toLowerCase())) {
|
||||
return "0"
|
||||
if (!lootCodes.containsKey(code.toLowerCase())) {
|
||||
printWarning("Unknown loot-code '$code'")
|
||||
return "NULL"
|
||||
return "0"
|
||||
}
|
||||
if (strings)
|
||||
return "@${strings[code.toLowerCase()]}"
|
||||
else
|
||||
return escapeString(code.toLowerCase())
|
||||
return "LOOT_${humanNameToSymbol(code.toLowerCase(), true)}"
|
||||
}
|
||||
|
||||
def extractEnemyStrings(row, strings)
|
||||
|
@ -3216,12 +3216,18 @@ class A2PackPartitions
|
|||
def experience = row.@experience; assert experience
|
||||
def mapCode = row.@"map-code"; assert mapCode
|
||||
def groupSize = row.@"group-size"; assert groupSize
|
||||
def lootChance = row.@"loot-chance"; // optional, defaults to 10%
|
||||
lootChance = "100"; println("FIXME: setting lootChance to 100")
|
||||
def lootCode = row.@"loot-code" // optional
|
||||
def lootChances = row.@"loot-chance"; // optional, defaults to 10%
|
||||
def lootCodes = row.@"loot-code" // optional
|
||||
def goldLoot = row.@"gold-loot"; assert goldLoot
|
||||
def gangChance = row.@"gang-chance"; // optional
|
||||
|
||||
def parsedLootCodes = (lootCodes ? lootCodes : "").split(/[,.;]/).collect { validateLootCode(it.trim()) }
|
||||
if (parsedLootCodes.size() > 2)
|
||||
throw new Exception("Max of two loot codes allowed")
|
||||
def parsedLootChances = (lootChances ? lootChances : "").split(/[,.;]/).collect { it.toInteger() }
|
||||
if (parsedLootChances.size() > parsedLootCodes.size())
|
||||
throw new Exception("Excess loot chances - or not enough loot codes")
|
||||
|
||||
out.println("word = " +
|
||||
"@${strings[name]}, ${parseDice(hitPoints)} // name, hit dice")
|
||||
out.println("byte = PO${humanNameToSymbol(image1, false)}, " +
|
||||
|
@ -3233,9 +3239,11 @@ class A2PackPartitions
|
|||
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("byte = ${parsedLootChances.size() >= 1 ? parsedLootChances[0] : 10}, " +
|
||||
"${parsedLootChances.size() >= 2 ? parsedLootChances[1] : 10} // loot chances")
|
||||
out.println("byte = ${parsedLootCodes.size() >= 1 ? parsedLootCodes[0] : "NULL"}, " +
|
||||
"${parsedLootCodes.size() >= 2 ? parsedLootCodes[1] : "NULL"} // loot codes")
|
||||
out.println("word = ${parseDice(goldLoot)} // goldLoot")
|
||||
out.println("byte = ${gangChance ? gangChance.toInteger() : 0} // gang chance")
|
||||
out.println("")
|
||||
|
||||
|
@ -3310,6 +3318,7 @@ class A2PackPartitions
|
|||
out.println()
|
||||
out.println("include \"globalDefs.plh\"")
|
||||
out.println("include \"gen_images.plh\"")
|
||||
out.println("include \"gen_items.plh\"")
|
||||
out.println()
|
||||
|
||||
def strings = [:]
|
||||
|
@ -3637,9 +3646,12 @@ class A2PackPartitions
|
|||
def storeAmount = parseWordAttr(row, "store-amount")
|
||||
def lootAmount = parseDiceAttr(row, "loot-amount")
|
||||
|
||||
if ("$kind, $modifier, $count, $storeAmount, $lootAmount" != ", NULL, 0, 0, 0")
|
||||
if ("$kind, $modifier, $count, $storeAmount, $lootAmount" != ", NULL, 0, 0, 0") {
|
||||
if (count > 0 && !name.contains("("))
|
||||
printWarning("countable item should have (singular/plural) in name")
|
||||
out.println(" return makeFancyItem(${escapeString(name)}, $price, " +
|
||||
"@${strings[kind]}, $modifier, $count, $storeAmount, $lootAmount)")
|
||||
}
|
||||
else
|
||||
out.println(" return makePlainItem(${escapeString(name)}, $price)")
|
||||
}
|
||||
|
@ -3740,10 +3752,17 @@ class A2PackPartitions
|
|||
{
|
||||
out.println("def $funcName(code)#1")
|
||||
codeToFunc.sort().each { code, funcs ->
|
||||
def s = (strings != null && strings[code]) ? "@${strings[code]}" : escapeString(code)
|
||||
out.println(" if streqi(code, $s); return @$prefix${humanNameToSymbol(code, false)}; fin")
|
||||
if (prefix == "lootCode_")
|
||||
out.println(" if code == LOOT_${humanNameToSymbol(code, true)}; return @$prefix${humanNameToSymbol(code, false)}; fin")
|
||||
else {
|
||||
def s = (strings != null && strings[code]) ? "@${strings[code]}" : escapeString(code)
|
||||
out.println(" if streqi(code, $s); return @$prefix${humanNameToSymbol(code, false)}; fin")
|
||||
}
|
||||
}
|
||||
out.println(" puts(code)")
|
||||
if (prefix == "lootCode_")
|
||||
out.println(" printf1(\"%d\", code)")
|
||||
else
|
||||
out.println(" puts(code)")
|
||||
out.println(" return fatal(\"$funcName\")")
|
||||
out.println("end\n")
|
||||
}
|
||||
|
@ -3793,7 +3812,7 @@ class A2PackPartitions
|
|||
addCodeToFunc(itemNum, row.@"loot-code", lootCodeToItemNums) // no underscore because item num instead of func
|
||||
addCodeToFunc(itemNum, row.@"store-code", storeCodeToItemNums) // ...ditto
|
||||
}
|
||||
allLootCodes = (lootCodeToItemNums.keySet().collect{ it.toLowerCase() }) as Set
|
||||
lootCodeToItemNums.keySet().each { lootCodes[it.toLowerCase()] = [num: lootCodes.size() + 1] }
|
||||
|
||||
// Make constants for the function table
|
||||
new File("build/src/plasma/gen_items.plh.new").withWriter { out ->
|
||||
|
@ -3801,6 +3820,12 @@ class A2PackPartitions
|
|||
out.println("const items_forLootCode = 0")
|
||||
out.println("const items_forStoreCode = 2")
|
||||
out.println("")
|
||||
|
||||
lootCodes.keySet().sort().each {
|
||||
out.println("const LOOT_${humanNameToSymbol(it, true)} = ${lootCodes[it].num}")
|
||||
}
|
||||
out.println("")
|
||||
|
||||
items.each { typeName, itemNum, index, row ->
|
||||
out.println("const ${itemNum} = ${index}")
|
||||
if (index == nWeapons)
|
||||
|
|
|
@ -30,8 +30,10 @@ struc EnemyData
|
|||
word r_en_dmgDice
|
||||
word r_en_expDice
|
||||
word r_en_groupSize
|
||||
byte b_en_lootChance
|
||||
word s_en_lootCode
|
||||
byte b_en_lootChance0
|
||||
byte b_en_lootChance1
|
||||
byte b_en_lootCode0
|
||||
byte b_en_lootCode1
|
||||
word r_en_goldLoot
|
||||
byte b_en_gangChance
|
||||
end
|
||||
|
@ -52,6 +54,8 @@ byte isAdvancing
|
|||
byte[] S_AN = "an "
|
||||
byte[] S_A = "a "
|
||||
byte[] S_EMPTY = ""
|
||||
byte[] S_COMMA = ", "
|
||||
byte[] S_AND = " and "
|
||||
|
||||
// To save time, we preload global funcs
|
||||
const NUM_PRELOADS = 4
|
||||
|
@ -976,8 +980,10 @@ def makeEnemy(pData)
|
|||
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->b_lootChance0 = pData->b_en_lootChance0
|
||||
p->b_lootChance1 = pData->b_en_lootChance1
|
||||
p->b_lootCode0 = pData->b_en_lootCode0
|
||||
p->b_lootCode1 = pData->b_en_lootCode1
|
||||
p=>r_goldLoot = pData=>r_en_goldLoot
|
||||
return p
|
||||
end
|
||||
|
@ -1096,69 +1102,147 @@ def youFind(pItem, suffix)
|
|||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def addIfPossible(pl, pItem)#1
|
||||
word pComp
|
||||
pComp = scanForNamedObj(pl=>p_items, pItem=>s_name)
|
||||
if pComp
|
||||
if pItem->t_type == TYPE_FANCY_ITEM
|
||||
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
|
||||
return youFind(pItem, "")
|
||||
// Returns: 0 = dupe
|
||||
// -1 = no room
|
||||
// 1 = added normally
|
||||
// 2 = override-added quest item
|
||||
// 3 = combined with existing stackable
|
||||
def fullAddItem(pItem, doit)
|
||||
word pPlayer, pComp, addToPlayer
|
||||
pPlayer = global=>p_players
|
||||
addToPlayer = NULL
|
||||
while pPlayer
|
||||
pComp = scanForNamedObj(pPlayer=>p_items, pItem=>s_name)
|
||||
if pComp
|
||||
if pComp->t_type == TYPE_FANCY_ITEM and pComp=>w_count > 0
|
||||
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
|
||||
return 3 // combined
|
||||
fin
|
||||
if !pComp=>w_price
|
||||
return 0 // dupe quest item
|
||||
fin
|
||||
fin
|
||||
if !addToPlayer and roomInPack(pPlayer); addToPlayer = pPlayer; fin
|
||||
pPlayer = pPlayer=>p_nextObj
|
||||
loop
|
||||
|
||||
if addToPlayer
|
||||
if doit; addToList(@addToPlayer=>p_items, pItem); fin
|
||||
return 1
|
||||
fin
|
||||
|
||||
if pItem=>w_price
|
||||
return -1 // no room, and item has a price so it's not a quest item
|
||||
fin
|
||||
|
||||
// Override: quest items allowed to exceed pack limitations
|
||||
if doit; addToList(@global=>p_players=>p_items, pItem); fin
|
||||
return 2
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def lootItem(ppItems, lootChance, lootCode)#0
|
||||
word pItemTblModule, itemNum, pItem, pComp
|
||||
|
||||
if lootCode == 0; return; fin
|
||||
if rollPercentileWithLuck(-(global=>p_players->b_luck)) >= lootChance; return; fin
|
||||
|
||||
mmgr(START_LOAD, 1) // code is in partition 1
|
||||
pItemTblModule = mmgr(QUEUE_LOAD, MOD_GEN_ITEM_TABLES<<8 | RES_TYPE_MODULE)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
|
||||
itemNum = randomFromArray(pItemTblModule()=>items_forLootCode(lootCode), NULL)
|
||||
pItem = createItem(itemNum)
|
||||
if pItem->t_type == TYPE_FANCY_ITEM
|
||||
pItem=>w_count = rollDiceWithLuck(pItem=>r_lootAmount, global=>p_players->b_luck)
|
||||
fin
|
||||
|
||||
mmgr(FREE_MEMORY, pItemTblModule)
|
||||
|
||||
pComp = scanForNamedObj(*ppItems, pItem=>s_name)
|
||||
if pComp and pItem->t_type == TYPE_FANCY_ITEM
|
||||
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
|
||||
elsif fullAddItem(pItem, FALSE) >= 1 // trial-run, don't add to main inv
|
||||
addToList(ppItems, pItem)
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def calcListSuffix(pThing, final)#1
|
||||
if pThing=>p_nextObj
|
||||
if pThing=>p_nextObj=>p_nextObj
|
||||
return @S_COMMA
|
||||
else
|
||||
return FALSE // duplicate item
|
||||
return @S_AND
|
||||
fin
|
||||
fin
|
||||
if roomInPack(pl)
|
||||
addToList(@pl=>p_items, pItem)
|
||||
return youFind(pItem, "")
|
||||
fin
|
||||
return FALSE // no room
|
||||
return final
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def addItem(pItem)#0
|
||||
byte ok
|
||||
ctx = pItem; ok = first(global=>p_players, &(pl) addIfPossible(pl, ctx))
|
||||
if ok; return; fin
|
||||
if first(global=>p_players, &(pl) scanForNamedObj(pl=>p_items, ctx=>s_name))
|
||||
return // duplicate item, silently pretend it didn't happen
|
||||
fin
|
||||
youFind(pItem, " but have no room")
|
||||
def dispLoot(pItems)#0
|
||||
word pItem, final, suffix
|
||||
final = "!\n"
|
||||
displayStr("You find ")
|
||||
pItem = pItems
|
||||
while pItem
|
||||
suffix = calcListSuffix(pItem, final)
|
||||
if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
|
||||
isPlural = TRUE
|
||||
displayf3("%d %s%s", pItem=>w_count, pItem=>s_name, suffix)
|
||||
else
|
||||
isPlural = FALSE
|
||||
displayf2("%s%s", pItem=>s_name, suffix)
|
||||
fin
|
||||
pItem = pItem=>p_nextObj
|
||||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def collectLootAndXP()#2
|
||||
word group, enemy, gold, xp, pItemTblModule, itemNum, pItem
|
||||
byte lootedItem
|
||||
def collectLootAndXP()#0
|
||||
word group, enemy, gold, xp, pItems, pItem, first
|
||||
|
||||
gold = 0
|
||||
xp = 0
|
||||
pItems = NULL
|
||||
group = global=>p_enemyGroups
|
||||
lootedItem = FALSE
|
||||
while group
|
||||
enemy = group=>p_enemies
|
||||
if enemy
|
||||
lootItem(@pItems, enemy->b_lootChance0, enemy->b_lootCode0)
|
||||
lootItem(@pItems, enemy->b_lootChance1, enemy->b_lootCode1)
|
||||
fin
|
||||
while enemy
|
||||
gold = gold + rollDiceWithLuck(enemy=>r_goldLoot, global=>p_players->b_luck)
|
||||
xp = xp + rollDiceWithLuck(enemy=>r_enemyXP, global=>p_players->b_luck)
|
||||
if !lootedItem and enemy=>s_lootCode and enemy->b_lootChance
|
||||
if rollPercentileWithLuck(-(global=>p_players->b_luck)) < enemy->b_lootChance
|
||||
mmgr(START_LOAD, 1) // code is in partition 1
|
||||
pItemTblModule = mmgr(QUEUE_LOAD, MOD_GEN_ITEM_TABLES<<8 | RES_TYPE_MODULE)
|
||||
mmgr(FINISH_LOAD, 0)
|
||||
itemNum = randomFromArray(pItemTblModule()=>items_forLootCode(enemy=>s_lootCode), NULL)
|
||||
pItem = createItem(itemNum)
|
||||
if pItem->t_type == TYPE_FANCY_ITEM
|
||||
pItem=>w_count = rollDiceWithLuck(pItem=>r_lootAmount, global=>p_players->b_luck)
|
||||
fin
|
||||
addItem(pItem)
|
||||
mmgr(FREE_MEMORY, pItemTblModule)
|
||||
lootedItem = TRUE
|
||||
fin
|
||||
fin
|
||||
enemy = enemy=>p_nextObj
|
||||
loop
|
||||
group = group=>p_nextObj
|
||||
loop
|
||||
return gold, xp
|
||||
|
||||
if pItems
|
||||
dispLoot(pItems)
|
||||
// When taking out of one list and into another, we can't use forEach, because the p_nextObj
|
||||
// link gets destroyed as we go along.
|
||||
while pItems
|
||||
pItem = pItems
|
||||
pItems = pItems=>p_nextObj
|
||||
fullAddItem(pItem, TRUE)
|
||||
loop
|
||||
fin
|
||||
|
||||
if gold > 0
|
||||
displayf1("You collect %d gold!\n", addGold(gold))
|
||||
fin
|
||||
|
||||
if xp > 0
|
||||
addXP_all(xp)
|
||||
displayf1("You earn %d experience!\n", xp); first = FALSE
|
||||
fin
|
||||
|
||||
if pItems or gold or xp
|
||||
promptAnyKey(FALSE)
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1262,8 +1346,6 @@ end
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def processWin()#0
|
||||
word gold, xp, player, tmp
|
||||
|
||||
// Clear the window, then display the win message
|
||||
clearWindow
|
||||
callGlobalFunc(GS_COMBAT_WIN, 0, 0, 0)
|
||||
|
@ -1276,20 +1358,8 @@ def processWin()#0
|
|||
clearWindow
|
||||
fin
|
||||
|
||||
// Make max space avail for item tables.
|
||||
freePreloaded
|
||||
|
||||
// Grab the loot
|
||||
gold, xp = collectLootAndXP()
|
||||
if gold > 0
|
||||
displayf1("You find %d gold! ", addGold(gold))
|
||||
fin
|
||||
if xp > 0
|
||||
addXP_all(xp)
|
||||
displayf1("You earn %d experience! ", xp)
|
||||
fin
|
||||
promptAnyKey(FALSE)
|
||||
|
||||
// Make max space avail for item tables, then grab the loot
|
||||
collectLootAndXP()
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1316,10 +1386,13 @@ def _combat_zoneEncounter(s_encZone)#1
|
|||
|
||||
// Tell what the party currently faces
|
||||
clearWindow()
|
||||
displayOpponents()
|
||||
|
||||
// Make sure portrait reflects a living enemy
|
||||
setPortrait(first(global=>p_enemyGroups, @groupCanFight)=>p_enemies->b_image)
|
||||
p = first(global=>p_enemyGroups, @groupCanFight)
|
||||
if p
|
||||
displayOpponents()
|
||||
setPortrait(p=>p_enemies->b_image)
|
||||
fin
|
||||
|
||||
// Get the choice of each player or NPC
|
||||
isAdvancing = FALSE
|
||||
|
|
|
@ -593,7 +593,7 @@ end
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Select an item and use it. Returns item if it needs to be processed by outer loop, else NULL
|
||||
def doUseItem(player, item)#1
|
||||
word pMod, oldVal, newVal
|
||||
word pMod, oldVal, newVal, name
|
||||
if item->t_type == TYPE_FANCY_ITEM and item=>p_modifiers
|
||||
clearMenuRect()
|
||||
clearMainRect()
|
||||
|
@ -616,7 +616,11 @@ def doUseItem(player, item)#1
|
|||
pause(800)
|
||||
return NULL
|
||||
fin
|
||||
return item=>s_name // general 'use' handled by outer engine, because it might involve graphics
|
||||
|
||||
// General 'use' handled by outer engine, because it might involve graphics.
|
||||
// Depluralize the name on the way out, because scripts will match on that.
|
||||
isPlural = FALSE
|
||||
return mmgr(HEAP_INTERN, sprintf1("%s", item=>s_name))
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -188,8 +188,10 @@ struc TEnemy
|
|||
word r_enemyDmg // 3 hex digits: num dice, die size, add. E.g. $361 = 3d6+1
|
||||
word r_enemyXP // 3 hex digits: num dice, die size, add. E.g. $361 = 3d6+1
|
||||
word r_groupSize // number encountered, as 3 hex digits for dice
|
||||
byte b_lootChance // % chance for loot item
|
||||
word s_lootCode // code in loot table
|
||||
byte b_lootChance0 // % chance for first loot item
|
||||
byte b_lootChance1 // % chance for second loot item
|
||||
byte b_lootCode0 // code in loot table for first loot item
|
||||
byte b_lootCode1 // code in loot table for second loot item
|
||||
word r_goldLoot // monetary loot when killed, as 3 hex digits for dice
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user