From 68399614a56ec18523d946740def7fb387670d2d Mon Sep 17 00:00:00 2001 From: Martin Haye Date: Wed, 13 Sep 2017 08:06:52 -0700 Subject: [PATCH] Refactor to use lambdas for list iteration everywhere it's reasonable. --- Platform/Apple/virtual/src/plasma/combat.pla | 115 ++++----- Platform/Apple/virtual/src/plasma/gamelib.plh | 14 +- .../Apple/virtual/src/plasma/gameloop.pla | 236 +++++++----------- Platform/Apple/virtual/src/plasma/party.pla | 181 +++++--------- Platform/Apple/virtual/src/plasma/store.pla | 17 +- 5 files changed, 223 insertions(+), 340 deletions(-) diff --git a/Platform/Apple/virtual/src/plasma/combat.pla b/Platform/Apple/virtual/src/plasma/combat.pla index 7b1dfb78..b9a10c0b 100644 --- a/Platform/Apple/virtual/src/plasma/combat.pla +++ b/Platform/Apple/virtual/src/plasma/combat.pla @@ -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() diff --git a/Platform/Apple/virtual/src/plasma/gamelib.plh b/Platform/Apple/virtual/src/plasma/gamelib.plh index 8a1b24ca..a5ff35b9 100644 --- a/Platform/Apple/virtual/src/plasma/gamelib.plh +++ b/Platform/Apple/virtual/src/plasma/gamelib.plh @@ -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 ////////////// diff --git a/Platform/Apple/virtual/src/plasma/gameloop.pla b/Platform/Apple/virtual/src/plasma/gameloop.pla index 685013b7..046d25ed 100644 --- a/Platform/Apple/virtual/src/plasma/gameloop.pla +++ b/Platform/Apple/virtual/src/plasma/gameloop.pla @@ -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 diff --git a/Platform/Apple/virtual/src/plasma/party.pla b/Platform/Apple/virtual/src/plasma/party.pla index ddd4edba..fad02575 100644 --- a/Platform/Apple/virtual/src/plasma/party.pla +++ b/Platform/Apple/virtual/src/plasma/party.pla @@ -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 /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Platform/Apple/virtual/src/plasma/store.pla b/Platform/Apple/virtual/src/plasma/store.pla index 14552bfd..b9808690 100644 --- a/Platform/Apple/virtual/src/plasma/store.pla +++ b/Platform/Apple/virtual/src/plasma/store.pla @@ -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')