Refactor to use lambdas for list iteration everywhere it's reasonable.

This commit is contained in:
Martin Haye 2017-09-13 08:06:52 -07:00
parent 082bd4bf22
commit 68399614a5
5 changed files with 223 additions and 340 deletions

View File

@ -108,15 +108,7 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
def scanModifiers(pMod, s_match)
word sum
sum = 0
while pMod
if streqi(pMod=>s_name, s_match)
sum = sum + pMod=>w_modValue
fin
pMod = pMod=>p_nextObj
loop
return sum
ctx = s_match; return sum(pMod, &(p) streqi(p=>s_name, ctx), &(p) p=>w_modValue)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -174,6 +166,13 @@ def rollEnemyDodge(pPlayer, pEnemy, sAction)
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def killEnemy(pEnemy)#0
pEnemy=>w_health = 0
printf1(" %s is killed!", pEnemy=>s_name)
nEnemiesFighting = nEnemiesFighting - 1
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def damageEnemy(pPlayer, pEnemy, dmg, sAction)#0
if combatDebug
@ -184,12 +183,10 @@ def damageEnemy(pPlayer, pEnemy, dmg, sAction)#0
buildString(@addToString)
printf3("\n%s %s %s ", pPlayer=>s_name, sAction, pEnemy=>s_name)
printf1("for %d damage.", dmg)
if pEnemy=>w_health <= dmg
pEnemy=>w_health = 0
printf1(" %s is killed!", pEnemy=>s_name)
nEnemiesFighting = nEnemiesFighting - 1
else
if pEnemy=>w_health > dmg
pEnemy=>w_health = pEnemy=>w_health - dmg
else
killEnemy(pEnemy)
fin
puts("\n")
displayStr(finishString(0))
@ -307,6 +304,12 @@ def playerShoot(pPlayer, pWeapon)#0
damageEnemy(pPlayer, pEnemy, dmg, sAction)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def ammoMatches(ammo, kind)
if ammo->t_type <> TYPE_FANCY_ITEM; return FALSE; fin
return streqi(ammo=>s_itemKind, kind)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Reloads the given weapon, as full as it can be given the player's ammo supply.
def reload(pl, pWeapon, echo)#0
@ -322,13 +325,7 @@ def reload(pl, pWeapon, echo)#0
fin
// Find matching ammo
item = pl=>p_items
while item
if item->t_type == TYPE_FANCY_ITEM
if streqi(item=>s_itemKind, pWeapon=>s_ammoKind); break; fin
fin
item = item=>p_nextObj
loop
ctx = pWeapon; item = first(pl=>p_items, &(p) ammoMatches(p, ctx=>s_ammoKind))
if !item
if echo
displayf3("\n%s has no ammo for %s %s!\n", pl=>s_name, hisHerTheir(pl->c_gender), pWeapon=>s_name))
@ -373,22 +370,22 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayOpponents()#0
word p
byte count, first
byte count, firstTime
byte isPlural
displayStr("You face ")
first = TRUE
firstTime = TRUE
p = global=>p_enemyGroups
while p
if !first
if !firstTime
if p=>p_nextObj
displayStr(", ")
else
displayStr(" and ")
fin
fin
first = FALSE
count = countListFiltered(p=>p_enemies, p_nextObj, @canFight)
firstTime = FALSE
count = sum(p=>p_enemies, @canFight, @trueFunc)
isPlural = count <> 1
if (p=>p_enemies=>r_groupSize == 0)
displayf2("%s at %d'", p=>p_enemies=>s_name, p->b_enemyGroupRange)
@ -410,19 +407,13 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
def minEnemyDist()#1
word p, p2, anybodyAlive, minDist
word p, anybodyAlive, minDist
minDist = 9999
// Process each group
p = global=>p_enemyGroups
while p
p2 = p=>p_enemies
anybodyAlive = FALSE
while p2
if canFight(p2); anybodyAlive = TRUE; fin
p2 = p2=>p_nextObj
loop
if anybodyAlive
if first(p=>p_enemies, @canFight)
minDist = min(minDist, p->b_enemyGroupRange)
fin
p = p=>p_nextObj
@ -640,11 +631,7 @@ def playerCombatTurn(pl)#0
if !nEnemiesFighting; return; fin
// Get weapon
pWeapon = pl=>p_items
while pWeapon
if pWeapon->t_type == TYPE_WEAPON and pWeapon->b_flags & ITEM_FLAG_EQUIP; break; fin
pWeapon = pWeapon=>p_nextObj
loop
pWeapon = first(pl=>p_items, &(p) p->t_type == TYPE_WEAPON and p->b_flags & ITEM_FLAG_EQUIP)
// Execute the player's choice
when pl->b_combatChoice
@ -697,10 +684,9 @@ def enemyCombatTurn(pe)#1
word pl, dodgeChance, pItem, damReduce
byte roll, dam, needShow
// Choose a target
pl = randomFromListFiltered(global=>p_players, p_nextObj, @canFight)
if !pl; return FALSE; fin
// Choose a random target from amongst the fighting players
roll = rand16() % nPlayersFighting
pl = select(global=>p_players, @canFight, roll)
isPlural = FALSE
displayf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name)
@ -747,6 +733,7 @@ def enemyCombatTurn(pe)#1
pl=>w_health = max(0, pl=>w_health - dam)
if pl=>w_health == 0
displayf1(" %s is killed!", pl=>s_name)
nPlayersFighting--
fin
needShow = TRUE
fin
@ -836,6 +823,17 @@ def makeEnemyGroup(enemyFunc)#1
return p
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Return a random entry from an array which is terminated by a zero entry
def randomFromArray(arr)#1
byte siz
siz = 0
while *((siz << 1) + arr)
siz++
loop
return *(((rand16() % siz) << 1) + arr)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def makeRandomGroup(mapCode)#0
word enemiesModule
@ -994,27 +992,6 @@ def startCombat(mapCode)#1
return 0 // just to keep compiler happy
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// For cheating in god mode, kill all enemies. This is better than just setting nEnemiesFighting
// to zero, because we'll still get loot.
def killAllEnemies()#0
word pGroup, pEnemy
pGroup = global=>p_enemyGroups
// For every group...
while pGroup
pEnemy = pGroup=>p_enemies
// For every enemy in the group...
while pEnemy
pEnemy=>w_health = 0 // kill em
pEnemy = pEnemy=>p_nextObj
loop
pGroup = pGroup=>p_nextObj
loop
// Update nEnemiesFighting
determineCombatOrder()
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def processWin()#0
word gold, xp
@ -1040,7 +1017,7 @@ def _combat_zoneEncounter(s_encZone)#1
// Show portrait and threat details, find out if player wants to fight (vs. run)
answer = startCombat(s_encZone)
if answer == 99
killAllEnemies()
forEach(global=>p_enemyGroups, &(g) forEach(g=>p_enemies, @killEnemy))
elsif answer == -99
return answer // special code for death
elsif !answer
@ -1061,13 +1038,7 @@ def _combat_zoneEncounter(s_encZone)#1
rawDisplayStr("\n")
// Get the choice of each player or NPC
p = global=>p_players
while p
if canFight(p) and nEnemiesFighting and !isFleeing
playerCombatChoose(p)
fin
p = p=>p_nextObj
loop
forSome(global=>p_players, &(pl) canFight(pl) and nEnemiesFighting and !isFleeing, @playerCombatChoose)
clearWindow()
determineCombatOrder()

View File

@ -36,9 +36,7 @@ import gamelib
predef clearEncounterZones()#0
predef clearPortrait()#0
predef clearWindow()#0
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
@ -46,9 +44,13 @@ import gamelib
predef displayf3(fmt, arg1, arg2, arg3)#0
predef displayStr(str)#0
predef encodeDice(nDice, dieSize, add)#1
predef equipItem(item)#0
predef fatal(msg)#1
predef finishString(isPlural)#1
predef flipToPage1()#0
predef first(p, sel)#1
predef forEach(p, do)#0
predef forSome(p, sel, do)#0
predef getCharResponse()#1
predef getCursor()#2
predef getDir()#1
@ -61,6 +63,7 @@ import gamelib
predef girdPlayer(player)#0
predef giveItemToPlayer(p_player, itemFuncNum)#0
predef hisHerTheir(c_gender)#1
predef index(p, num)#1
predef initHeap(loadedSize)#0
predef initPlayerXP(player)#0
predef loadFrameImg(img)#0
@ -72,7 +75,6 @@ import gamelib
predef min(a, b)#1
predef mmgr(cmd, wordParam)#1
predef moveWayBackward()#1
predef numToPlayer(num)#1
predef parseDec(str)#1
predef partyHasPlayer(playerName)#1
predef pause(count)#0
@ -87,8 +89,6 @@ import gamelib
predef queue_setMap(is3D, num, x, y, dir)#0
predef queue_teleport(x, y, dir)#0
predef rand16()#1
predef randomFromArray(arr)#1
predef randomFromListFiltered(p, offset, filterFunc)#1
predef rawDisplayf1(fmt, arg1)#0
predef rawDisplayf2(fmt, arg1, arg2)#0
predef rawDisplayf3(fmt, arg1, arg2, arg3)#0
@ -108,6 +108,7 @@ import gamelib
predef scriptEvent(event, param)#0
predef scriptSetAvatar(avatarTileNum)#0
predef scriptSwapTile(fromX, fromY, toX, toY)#0
predef select(p, sel, num)#1
predef setCmd(key, func)#0
predef setCursor(x, y)#0
predef setGameFlag(flagName, val)#0
@ -130,9 +131,11 @@ import gamelib
predef sprintf2(fmt, arg1, arg2)#1
predef sprintf3(fmt, arg1, arg2, arg3)#1
predef streqi(a, b)#1
predef sum(p, sel, func)#1
predef takeItemFromPlayer(p_player, itemName)#0
predef textHome()#0
predef tossStrings()#0
predef trueFunc()#1
predef unbenchPlayer()#0
// This pointer is the root of all heap-tracked (and garbage collected) objects.
@ -148,6 +151,7 @@ import gamelib
word pGodModule
word typeHash
byte isPlural
word ctx
/////////// Shared string constants //////////////

View File

@ -124,6 +124,9 @@ byte animDirCt
byte anyAnims = TRUE
word animPauseCt
// Context for lambda functions (in lieu of closures, for now at least)
export word ctx
// Shared string constants
export byte[] S_INTELLIGENCE = "Intelligence"
export byte[] S_STRENGTH = "Strength"
@ -941,39 +944,41 @@ export asm streqi(a, b)#1
+asmPlasmRet 2
sta tmp
sty tmp+1
beq .noteqi ; NULL is never equal
lda evalStkL+1,x
sta pTmp
lda evalStkH+1,x
sta pTmp+1
beq .noteqi ; NULL is never equal
ldy #0
lda (tmp),y
cmp (pTmp),y
beq + ; if lengths not equal, strings can't be equal
--
beq + ; if lengths not equal, strings can't be equal
.noteqi
lda #0
tay
rts
+ tax ; count up to (verified same) length of the strings
+ tax ; count up to (verified same) length of the strings
- iny
lda (tmp),y
cmp #('z'&$7F)+1 ; convert to upper case
cmp #('z'&$7F)+1 ; convert to upper case
bcs +
cmp #'a'&$7F
bcc +
sbc #$20
+ sta ysav
lda (pTmp),y
cmp #('z'&$7F)+1 ; convert to upper case
cmp #('z'&$7F)+1 ; convert to upper case
bcs +
cmp #'a'&$7F
bcc +
sbc #$20
+ cmp ysav
bne -- ; abort on inequality
bne .noteqi ; abort on inequality
dex
bne -
lda #1
ldy #0 ; okay, they're equal. Return 1 (not just any char; so that PLASMA when...is can work)
ldy #0 ; okay, they're equal. Return 1 (not just any char; so that PLASMA when...is can work)
rts
end
@ -1227,6 +1232,57 @@ export def getCharResponse()#1
return mmgr(HEAP_INTERN, $200)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def trueFunc(x)#1
return 1
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def select(p, sel, num)#1
while p
if sel(p)
if !num; break; fin
num--
fin
p = p=>p_nextObj
loop
return p
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def first(p, sel)#1
return select(p, sel, 0)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def index(p, num)#1
return select(p, @trueFunc, num)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def sum(p, sel, func)#1
word sum
sum = 0
while p
if sel(p); sum = sum + func(p); fin
p = p=>p_nextObj
loop
return sum
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def forSome(p, sel, do)#0
while p
if sel(p); do(p)#0; fin
p = p=>p_nextObj
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def forEach(p, do)#0
forSome(p, @trueFunc, do)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Pause for a specified count period, advancing the animation periodically.
export def pause(count)#0
@ -1519,6 +1575,12 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Display the party data on the screen
def showPartyLine(p)#0
if p <> global=>p_players; displayChar('\n'); fin
if p->b_skillPoints; rawDisplayStr("^I"); fin // inverse for chars needing lvl-up
rawDisplayf1("%s^N", p=>s_name)
rightJustifyStr(sprintf2("%d/%d", p=>w_health, p=>w_maxHealth), CHAR_WND_HEALTH_X)
end
export def showParty()#0
word p, cursX, cursY
@ -1526,13 +1588,8 @@ export def showParty()#0
setWindow3()
clearWindow()
// Display header (or LEVEL UP message)
p = global=>p_players
while p
if p->b_skillPoints; break; fin
p = p=>p_nextObj
loop
if p
// Display header (or LEVEL UP message if any player has un-applied skill pts)
if first(global=>p_players, &(p) p->b_skillPoints)
rawDisplayStr("^Y^I LEVEL U)P ^N\n")
else
rawDisplayStr("^LName")
@ -1541,14 +1598,7 @@ export def showParty()#0
fin
// Display each character's name and health
p = global=>p_players
while p
if p <> global=>p_players; displayChar('\n'); fin
if p->b_skillPoints; rawDisplayStr("^I"); fin // inverse for chars needing lvl-up
rawDisplayf1("%s^N", p=>s_name)
rightJustifyStr(sprintf2("%d/%d", p=>w_health, p=>w_maxHealth), CHAR_WND_HEALTH_X)
p = p=>p_nextObj
loop
forEach(global=>p_players, @showPartyLine)
// Finish up
if mapIs3D and texturesLoaded; copyWindow(); fin
@ -2216,51 +2266,7 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def countList(p)#1
byte n
n = 0
while p
n++
p = p=>p_nextObj
loop
return n
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def countListFiltered(p, offset, filterFunc)#1
byte n
n = 0
while p
if filterFunc(p)
n++
fin
p = *(p + offset)
loop
return n
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Find the end of a null-terminated array
export def countArray(arr)#1
byte count
for count = 0 to 127
if !*arr; break; fin
arr = arr + 2
next
return count
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def randomFromListFiltered(p, offset, filterFunc)#1
byte n
n = rand16() % countListFiltered(p, offset, filterFunc)
while p
if filterFunc(p)
if n == 0; return p; fin
n--
fin
p = *(p + offset)
loop
return NULL
return sum(p, @trueFunc, @trueFunc)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2723,17 +2729,6 @@ export def initHeap(loadedSize)#0
fin
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Return a random entry from an array which is terminated by a zero entry
export def randomFromArray(arr)#1
byte siz
siz = 0
while *((siz << 1) + arr)
siz++
loop
return *(((rand16() % siz) << 1) + arr)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor: create a modifier given its name and value
export def makeModifier(name, value)#1
@ -2745,16 +2740,8 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Recalculate player's armor score based on their currently equipped armor
export def calcPlayerArmor(player)#0
word pItem
player->b_armor = 0
pItem = player=>p_items
while pItem
if pItem->t_type == TYPE_ARMOR and pItem->b_flags & ITEM_FLAG_EQUIP
player->b_armor = player->b_armor + pItem->b_armorValue
fin
pItem = pItem=>p_nextObj
loop
export def calcPlayerArmor(pl)#0
pl->b_armor = sum(pl=>p_items, &(p) p->t_type == TYPE_ARMOR and p->b_flags & ITEM_FLAG_EQUIP, &(p) p->b_armorValue)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2777,12 +2764,8 @@ export def payGold(amount)#1
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def scanForNamedObj(p_obj, name)#1
while p_obj
if streqi(p_obj=>s_name, name); return p_obj; fin
p_obj = p_obj=>p_nextObj
loop
return NULL
export def scanForNamedObj(list, name)#1
ctx = name; return first(list, &(p) streqi(p=>s_name, ctx))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2878,24 +2861,16 @@ export def initPlayerXP(player)#0
player=>w_nextXP = callGlobalFunc(GS_LEVEL_X_P, player->b_level + 1, 0, 0)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def equipItem(item)#0
item->b_flags = item->b_flags | ITEM_FLAG_EQUIP
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Arm newly created player (or NPC) with first weapon and all armor.
export def girdPlayer(player)#0
word pItem
byte weaponFound
weaponFound = FALSE
pItem = player=>p_items
while pItem
if pItem->t_type == TYPE_WEAPON
if !weaponFound
pItem->b_flags = pItem->b_flags | ITEM_FLAG_EQUIP
weaponFound = TRUE
fin
elsif pItem->t_type == TYPE_ARMOR
pItem->b_flags = pItem->b_flags | ITEM_FLAG_EQUIP
fin
pItem = pItem=>p_nextObj
loop
equipItem(first(player=>p_items, &(p) p->t_type == TYPE_WEAPON))
forSome(player=>p_items, &(p) p->t_type == TYPE_ARMOR, @equipItem)
calcPlayerArmor(player)
end
@ -2935,11 +2910,7 @@ export def takeItemFromPlayer(p_player, itemName)#0
if removeNamed(itemName, @p_player=>p_items); return; fin
// Fall back to all players, e.g. Prosp. Paul using his TNT.
p_player = global=>p_players
while p_player
if removeNamed(itemName, @p_player=>p_items); return; fin
p_player = p_player=>p_nextObj
loop
ctx = itemName; first(global=>p_players, &(p) removeNamed(ctx, @p=>p_items))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2950,12 +2921,12 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def playerHasItem(itemName)#1
return scanForNamedObj(global=>p_players=>p_items, itemName) <> NULL
ctx = itemName; return first(global=>p_players, &(p) scanForNamedObj(p=>p_items, ctx))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
export def partyHasPlayer(playerName)#1
return scanForNamedObj(global=>p_players, playerName) <> NULL
return scanForNamedObj(global=>p_players, playerName)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -2978,11 +2949,8 @@ export def getStat(player, statName)#1
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
pSkill = scanForNamedObj(player=>p_skills, statName)
if pSkill; return pSkill=>w_modValue; fin
puts(statName); return fatal("getStat")
end
@ -3019,15 +2987,12 @@ export def setStat(player, statName, val)#0
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
if streqi(statName, pSkill=>s_name)
pSkill=>w_modValue = max(0, val)
return
fin
pSkill = pSkill=>p_nextObj
loop
puts(statName); fatal("setStat")
pSkill = scanForNamedObj(player=>p_skills, statName)
if pSkill
pSkill=>w_modValue = max(0, val)
else
puts(statName); fatal("setStat")
fin
wend
end
@ -3088,17 +3053,6 @@ 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

View File

@ -58,16 +58,6 @@ word = @_displayItemStats, @_displayItemName
// Other global variables here
def travFind2(pObj, val, func)
word ret
while pObj
val, ret = func(val, pObj)#2
if ret; return pObj; fin
pObj = pObj=>p_nextObj
loop
return NULL
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def isEquipped(pItem)#1
if pItem->t_type == TYPE_ARMOR or pItem->t_type == TYPE_WEAPON
@ -76,29 +66,6 @@ def isEquipped(pItem)#1
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def itemByNum(player, num)#1
return travFind2(player=>p_items, num, &(n,p)(n-1, n==0))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Unequip item kind
def unequip(player, type, kind)#1
word item
item = player=>p_items
while item
if item->t_type == type
if (streqi(item=>s_itemKind, kind) or type == TYPE_WEAPON) and isEquipped(item)
item->b_flags = item->b_flags & ~ITEM_FLAG_EQUIP
break
fin
fin
item = item=>p_nextObj
loop
return item
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Display title of a column at top but below character name. Optionally append a second string,
// then leave a little vertical space below before beginning the content of the column.
@ -114,24 +81,18 @@ end
// Display inventory list page. Return number of items on page.
def showInventory(player, page, select)#1
word item
byte s_item, n_item, n_page, totalPages, first
byte s_item, n_item, n_page, totalPages, firstTime
s_item = 0
n_item = 0
n_page = page * INV_ROWS
item = player=>p_items
totalPages = (countList(item)+INV_ROWS-1) / INV_ROWS
totalPages = (countList(player=>p_items)+INV_ROWS-1) / INV_ROWS
showColumnTitle(INV_X-10, "Inventory", page+1, totalPages)
if page
while item and n_item < n_page
item = item=>p_nextObj
n_item++
loop
fin
first = TRUE
item = index(player=>p_items, n_page)
n_item = n_page
firstTime = TRUE
while item and n_item < (n_page + INV_ROWS)
if !first; displayChar('\n'); fin
first = FALSE
if !firstTime; displayChar('\n'); fin
firstTime = FALSE
if isEquipped(item)
rawDisplayStr("*")
@ -188,12 +149,7 @@ def showDerived(player)#0
rawDisplayf1("^T%DArmor\n", STATLBL_X)
// Get weapon
weapon = player=>p_items
while weapon
if weapon->t_type == TYPE_WEAPON and isEquipped(weapon); break; fin
weapon = weapon=>p_nextObj
loop
weapon = first(player=>p_items, &(w) w->t_type == TYPE_WEAPON and isEquipped(w))
if weapon
if weapon=>r_projectileDmg
displayDice(weapon=>r_projectileDmg)
@ -258,13 +214,8 @@ def showSkills(player)#0
if player->b_aiming; displaySkill(x1, @S_AIMING, @player->b_aiming, TRUE); fin
if player->b_handToHand; displaySkill(x1, @S_HAND_TO_HAND, @player->b_handToHand, TRUE); fin
if player->b_dodging; displaySkill(x1, @S_DODGING, @player->b_dodging, TRUE); fin
skill = player=>p_skills
while skill
if skill=>w_modValue > 0
displaySkill(x1, skill=>s_name, skill+w_modValue, TRUE)
fin
skill = skill=>p_nextObj
loop
ctx = x1
forSome(player=>p_skills, &(p) p=>w_modValue, &(p) displaySkill(ctx, p=>s_name, p+w_modValue, TRUE))
// Second column: attributes
showColumnTitle(STAT_X-5, "Attributes", 0, 0)
@ -401,10 +352,30 @@ def clearMainRect()#0
rawDisplayStr("^V000\n^J^J^J")
end
// Equip/unequip an item.
///////////////////////////////////////////////////////////////////////////////////////////////////
def armsMatch(i1, i2)
if i1 == i2 or i1->t_type <> i2->t_type or !isEquipped(i2); return FALSE; fin
if i1->t_type == TYPE_WEAPON; return TRUE; fin // can only equip 1 weapon at a time
return streqi(i1=>s_itemKind, i2=>s_itemKind) // type must be armor, since isEquipped is true
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def findMatchingEquip(player, item)
ctx = item; return first(player=>p_items, &(p) armsMatch(ctx, p)))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Equip/unequip a weapon or armor piece
def doEquip(player, item)#0
if unequip(player, item->t_type, item=>s_itemKind) <> item
item->b_flags = item->b_flags | ITEM_FLAG_EQUIP
word match
if item->b_flags & ITEM_FLAG_EQUIP
// Unequipping a piece
item->b_flags = item->b_flags & ~ITEM_FLAG_EQUIP
else
// Equipping a piece: be sure to unequip other matching piece (if any)
match = findMatchingEquip(player, item)
if match; match->b_flags = item->b_flags & ~ITEM_FLAG_EQUIP; fin
equipItem(item)
fin
calcPlayerArmor(player)
end
@ -441,17 +412,17 @@ end
// Trade an item to another player/npc
def doTrade(player, item)#1
word destPlayer
byte first, sel
byte firstTime, sel
clearMenuRect()
rawDisplayStr("To: ")
destPlayer = global=>p_players
sel = 0
first = TRUE
firstTime = TRUE
while destPlayer
if player <> destPlayer
if !first; rawDisplayStr(", "); fin
first = FALSE
if !firstTime; rawDisplayStr(", "); fin
firstTime = FALSE
rawDisplayf2("%d. %s", sel+1, destPlayer=>s_name)
fin
sel++
@ -462,7 +433,7 @@ def doTrade(player, item)#1
while TRUE
sel = getUpperKey()
if sel == $1B; return 0; fin
destPlayer = numToPlayer(sel-'1')
destPlayer = index(global=>p_players, sel-'1')
if destPlayer and destPlayer <> player
removeFromList(@player=>p_items, item)
addUnique(@destPlayer=>p_items, item)
@ -528,26 +499,23 @@ def doUse(player, item)#1
if item->t_type == TYPE_FANCY_ITEM and item=>p_modifiers
clearMenuRect()
clearMainRect()
pMod = item=>p_modifiers
while pMod
oldVal = getStat(player, pMod=>s_name)
setStat(player, pMod=>s_name, oldVal + pMod=>w_modValue)
newVal = getStat(player, pMod=>s_name)
rawDisplayStr(pMod=>s_name)
if newVal <> oldVal
takeItemFromPlayer(player, item=>s_name) // also handles reducing count of stackables
if newVal > oldVal
rawDisplayStr(" increased")
else
rawDisplayStr(" decreased")
fin
rawDisplayf2(" from %d to %d.", oldVal, newVal)
pMod = item=>p_modifiers // we only support one per item at present
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(" already at the limit.")
rawDisplayStr(" decreased")
fin
pause(800)
pMod = pMod=>p_nextObj
loop
rawDisplayf2(" from %d to %d.", oldVal, newVal)
else
rawDisplayStr(" already at the limit.")
fin
pause(800)
return NULL
fin
return item // general 'use' handled by outer engine, because it might involve graphics
@ -567,21 +535,6 @@ def doDestroy(player, item)#1
return FALSE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def matchEquipped(player, match)#1
word item
item = player=>p_items
while item
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
loop
return NULL
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayItems(pItem1, pItem2)#0
clearMainRect()
@ -598,21 +551,21 @@ def interactWithItem(player, item)#1
word comp, quantity
byte sel, ok
ok = TRUE
displayItems(item, matchEquipped(player, item))
displayItems(item, findMatchingEquip(player, item))
while TRUE
showItemMenu(item)
if !ok; beep; fin
ok = FALSE
sel = getUpperKey()
when sel
// Equip player with weapon/armor
// Equip/unequip player with weapon/armor
is 'E'
if (item->t_type == TYPE_ARMOR or item->t_type == TYPE_WEAPON)
doEquip(player, item)
return displayDone()
fin
break
// Use or Unequip an item
// Use an item
is 'U'
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
@ -628,14 +581,14 @@ def interactWithItem(player, item)#1
is 'S'
if isSplittable(item)
ok = doSplit(player, item)
if ok; displayItems(item, matchEquipped(player, item)); fin
if ok; displayItems(item, NULL); fin
fin
break
// Join stacks
is 'J'
if isJoinable(item)
ok = doJoin(player, item)
if ok; displayItems(item, matchEquipped(player, item)); fin
if ok; displayItems(item, NULL); fin
fin
break
// Destroy an item
@ -666,14 +619,14 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
i_page = 0
redisplay = 2
noRepeatMenu = FALSE
player = numToPlayer(player_num)
player = index(global=>p_players, player_num)
if player->b_skillPoints
mode = 'S' // go straight to level up if applicable
else
mode = 'I' // otherwise default to inventory mode
fin
repeat
player = numToPlayer(player_num)
player = index(global=>p_players, player_num)
if !player; beep; return NULL; fin
if redisplay >= 2
clearWindow()
@ -783,7 +736,7 @@ def _showPlayerSheet(player_num)#1 // funcTbl functions always have to return a
elsif mode == 'I'
sel = sel - 'A'
if sel >= 0 and sel < itemsOnPage
item = interactWithItem(player, itemByNum(player, i_page * INV_ROWS + sel))
item = interactWithItem(player, index(player=>p_items, 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--
@ -836,17 +789,7 @@ def selectPlayer(players)#1
// Then get a selection
rawDisplayStr("\nWhich character?")
n_item = getUpperKey() - 'A'
player = players
while player
if player->b_playerFlags & PLAYER_FLAG_NPC
if n_item == 0
break
fin
n_item--
fin
player = player=>p_nextObj
loop
return player
return select(players, &(p) p->b_playerFlags & PLAYER_FLAG_NPC, n_item)
end
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -46,6 +46,17 @@ pTmp = $4
ysav = $34
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Find the end of a null-terminated array
def countArray(arr)#1
byte count
for count = 0 to 127
if !*arr; break; fin
arr = arr + 2
next
return count
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def loadExtraModules()#0
mmgr(START_LOAD, 1) // code is in partition 1
@ -235,7 +246,7 @@ def storeSetup()#0
playerCount = countList(global=>p_players)
playerNum = 0
pPlayer = numToPlayer(playerNum)
pPlayer = index(global=>p_players, playerNum)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -264,7 +275,7 @@ def _buyFromStore(storeCode, profitPercent)#1
pageNum++
elsif choice >= '1' and (choice-'1') <= playerCount and (choice-'1') <> playerNum
playerNum = choice - '1'
pPlayer = numToPlayer(playerNum)
pPlayer = index(global=>p_players, playerNum)
elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
browseItem(choice-'A')
elsif choice == $1B // Esc
@ -364,7 +375,7 @@ def _sellToStore(profitPercent)#1
pageNum++
elsif choice >= '1' and (choice-'1') <= playerCount
playerNum = choice - '1'
pPlayer = numToPlayer(playerNum)
pPlayer = index(global=>p_players, playerNum)
totalItems = iterateSellables(9999, 0)
elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
sellItem(choice-'A')