mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-06-25 09:29:30 +00:00
Coded ammo consumption and reloading. Now to test test test.
This commit is contained in:
parent
07ba262072
commit
e2b0d2d51d
|
@ -2519,6 +2519,19 @@ end
|
|||
return val
|
||||
}
|
||||
|
||||
def parseBooleanAttr(row, attrName)
|
||||
{
|
||||
def val = parseStringAttr(row, attrName)
|
||||
return (val.toLowerCase() ==~ /yes|true|1/) ? "TRUE" : "FALSE"
|
||||
}
|
||||
|
||||
def parseGenderAttr(row, attrName)
|
||||
{
|
||||
def val = parseStringAttr(row, attrName)
|
||||
if (!val) return 0
|
||||
return val.charAt(0).toUpperCase()
|
||||
}
|
||||
|
||||
def parseModifier(row, attr1, attr2)
|
||||
{
|
||||
def bonusValue = parseWordAttr(row, attr1)
|
||||
|
@ -2550,7 +2563,8 @@ end
|
|||
"${parseByteAttr(row, "semi-auto-shots")}, " +
|
||||
"${parseByteAttr(row, "auto-shots")}, " +
|
||||
"${parseByteAttr(row, "range")}, " +
|
||||
"${escapeString(parseStringAttr(row, "combat-text"))})")
|
||||
"${escapeString(parseStringAttr(row, "combat-text"))}, " +
|
||||
"${parseBooleanAttr(row, 'single-use')})")
|
||||
}
|
||||
|
||||
def genArmor(func, row, out)
|
||||
|
@ -2603,7 +2617,8 @@ end
|
|||
"${Math.max(1, parseByteAttr(row, "level"))}, " +
|
||||
"${parseByteAttr(row, "aiming")}, " +
|
||||
"${parseByteAttr(row, "hand-to-hand")}, " +
|
||||
"${parseByteAttr(row, "dodging")})")
|
||||
"${parseByteAttr(row, "dodging")}, " +
|
||||
"${parseGenderAttr(row, "gender")})")
|
||||
row.attributes().sort().eachWithIndex { name, val, idx ->
|
||||
if (name =~ /^skill-(.*)/) {
|
||||
out.println(" addToList(@p=>p_skills, " +
|
||||
|
@ -2750,18 +2765,24 @@ def makeWeapon_pt1(name, kind, price, modifier, ammoKind, clipSize, meleeDmg, pr
|
|||
p=>p_modifiers = modifier
|
||||
p=>s_ammoKind = mmgr(HEAP_INTERN, ammoKind)
|
||||
p->b_clipSize = clipSize
|
||||
p->b_clipCurrent = clipSize
|
||||
p->b_clipCurrent = 0
|
||||
if !p->b_clipSize and projectileDmg
|
||||
p->b_clipCurrent = 1 // auto-reloading, e.g. bows
|
||||
fin
|
||||
p=>r_meleeDmg = meleeDmg
|
||||
p=>r_projectileDmg = projectileDmg
|
||||
return p
|
||||
end
|
||||
|
||||
def makeWeapon_pt2(p, attack0, attack1, attack2, weaponRange, combatText)
|
||||
def makeWeapon_pt2(p, attack0, attack1, attack2, weaponRange, combatText, singleUse)
|
||||
p->ba_attacks[0] = attack0
|
||||
p->ba_attacks[1] = attack1
|
||||
p->ba_attacks[2] = attack2
|
||||
p->b_weaponRange = weaponRange
|
||||
p=>s_combatText = mmgr(HEAP_INTERN, combatText)
|
||||
if singleUse
|
||||
p->b_flags = WEAPON_FLAG_SINGLE_USE
|
||||
fin
|
||||
return p
|
||||
end
|
||||
|
||||
|
@ -2887,7 +2908,7 @@ def makePlayer_pt1(name, intelligence, strength, agility, stamina, charisma, spi
|
|||
return p
|
||||
end
|
||||
|
||||
def makePlayer_pt2(p, health, level, aiming, handToHand, dodging)#1
|
||||
def makePlayer_pt2(p, health, level, aiming, handToHand, dodging, gender)#1
|
||||
p=>w_health = health
|
||||
p->b_level = level
|
||||
p=>w_maxHealth = health
|
||||
|
@ -2896,6 +2917,7 @@ def makePlayer_pt2(p, health, level, aiming, handToHand, dodging)#1
|
|||
p->b_aiming = aiming
|
||||
p->b_handToHand = handToHand
|
||||
p->b_dodging = dodging
|
||||
p->c_gender = gender
|
||||
initPlayerXP(p)
|
||||
return p
|
||||
end
|
||||
|
|
|
@ -123,9 +123,8 @@ def rollPlayerHit(pPlayer, pWeapon, pEnemy, sAction)
|
|||
if combatDebug; displayf1("Final chnc = %d%%\n", chance); fin
|
||||
|
||||
// See if it's a hit
|
||||
// TODO: factor in luck
|
||||
roll = rand16() % 100
|
||||
if combatDebug; displayf2("Roll=%d Hit=%d\n", roll, abs(roll < chance)); getUpperKey(); fin
|
||||
roll = rollPercentileWithLuck(-(pPlayer->b_luck)) // luck can reduce roll = increase chance to hit
|
||||
if combatDebug; displayf2("Roll=%d, need <%d\n", roll, abs(roll < chance)); getUpperKey(); fin
|
||||
if roll >= chance
|
||||
setPlural(0)
|
||||
displayf3("\n%s %s at %s but misses.\n", pPlayer=>s_name, sAction, pEnemy=>s_name)
|
||||
|
@ -201,8 +200,7 @@ def playerMelee(pPlayer, pWeapon)#0
|
|||
dmg = encodeDice(scanModifiers(pPlayer=>p_skills, @S_HAND_TO_HAND)+1, 4, 0)
|
||||
fin
|
||||
if combatDebug; displayf3("Base dmg: %dd%d+%d = ", (dmg>>12) & $F, (dmg >> 8) & $F, dmg & $FF); fin
|
||||
// TODO: factor in luck
|
||||
dmg = rollDice(dmg)
|
||||
dmg = rollDiceWithLuck(dmg, pPlayer->b_luck)
|
||||
if combatDebug; displayf1("%d\n", dmg); fin
|
||||
|
||||
// Damage bonus is:
|
||||
|
@ -259,8 +257,7 @@ def playerShoot(pPlayer, pWeapon)#0
|
|||
if combatDebug
|
||||
displayf3("Base dmg: %dd%d+%d = ", (pWeapon=>r_projectileDmg>>12) & $F, (pWeapon=>r_projectileDmg >> 8) & $F, pWeapon=>r_projectileDmg & $FF)
|
||||
fin
|
||||
// TODO: factor in luck
|
||||
dmg = rollDice(pWeapon=>r_projectileDmg)
|
||||
dmg = rollDiceWithLuck(pWeapon=>r_projectileDmg, pPlayer->b_luck)
|
||||
if combatDebug; displayf1("%d\n", dmg); fin
|
||||
|
||||
// Damage bonus is:
|
||||
|
@ -281,6 +278,56 @@ def playerShoot(pPlayer, pWeapon)#0
|
|||
damageEnemy(pPlayer, pEnemy, dmg, sAction)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reloads the given weapon, as full as it can be given the player's ammo supply.
|
||||
def reload(pl, pWeapon, echo)#0
|
||||
word item
|
||||
byte orig, n
|
||||
|
||||
setPlural(FALSE)
|
||||
|
||||
// If ammo type is null, it means weapon doesn't use ammo in traditional sense.
|
||||
if !pWeapon=>s_ammoKind
|
||||
pWeapon->b_clipCurrent = 1
|
||||
return
|
||||
fin
|
||||
|
||||
// Find matching ammo
|
||||
item = pl=>p_items
|
||||
while item
|
||||
if item->t_type == TYPE_STUFF
|
||||
if streqi(item=>s_itemKind, pWeapon=>s_ammoKind); break; fin
|
||||
fin
|
||||
item = item=>p_nextObj
|
||||
loop
|
||||
if !item
|
||||
displayf3("\n%s has no ammo for %s %s!\n", pl=>s_name, hisHerTheir(pl->c_gender), pWeapon=>s_name))
|
||||
return
|
||||
fin
|
||||
|
||||
// Transfer ammo to weapon
|
||||
n = max(item=>w_count, pWeapon->b_clipSize - pWeapon->b_clipCurrent)
|
||||
pWeapon->b_clipCurrent = pWeapon->b_clipCurrent + n
|
||||
item=>w_count = item=>w_count - n
|
||||
if item=>w_count == 0
|
||||
removeFromList(@pl=>p_items, item)
|
||||
fin
|
||||
if echo
|
||||
displayf3("\n%s reloads %s %s.\n", pl=>s_name, hisHerTheir(pl->c_gender), pWeapon=>s_name)
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def consumeAmmo(pl, pWeapon)#0
|
||||
if !pWeapon->b_clipCurrent; fatal("clip zero"); fin // sanity check
|
||||
pWeapon->b_clipCurrent = pWeapon->b_clipCurrent - 1
|
||||
|
||||
// Special (for e.g. bows): clipSize zero means weapon reloads automatically
|
||||
if !pWeapon->b_clipCurrent and !pWeapon->b_clipSize
|
||||
reload(pl, pWeapon, FALSE) // silently reload
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def playerDodge(pPlayer)#0
|
||||
// no need to display anything. Actual dodging mechanics handled in enemy attack function.
|
||||
|
@ -468,14 +515,15 @@ def playerCombatChoose(pl)#0
|
|||
rawDisplayf1("^D%s choice:", pl=>s_name)
|
||||
displayOption('M', "Melee")
|
||||
if pWeapon
|
||||
// Special (for e.g. bows): clipSize zero means weapon reloads automatically
|
||||
if pWeapon=>r_projectileDmg and !pWeapon->b_clipCurrent and !pWeapon->b_clipSize
|
||||
reload(pl, pWeapon, FALSE) // silently reload
|
||||
fin
|
||||
if pWeapon->b_clipCurrent
|
||||
canShoot = TRUE
|
||||
displayOption('S', "Shoot")
|
||||
fin
|
||||
if pWeapon->b_clipCurrent < pWeapon->b_clipSize
|
||||
// TODO: Need to check for enough ammo, and use it up.
|
||||
// TODO: If clip size is zero, weapon reloads automatically (e.g. bow with arrows)
|
||||
// TODO: Ammo kind is NULL, weapon doesn't use ammo at all
|
||||
canReload = TRUE
|
||||
displayOption('R', "Reload")
|
||||
fin
|
||||
|
@ -487,6 +535,7 @@ def playerCombatChoose(pl)#0
|
|||
displayOption('F', "Flee")
|
||||
canAdvance = minEnemyDist() > 5
|
||||
if canAdvance; displayOption('A', "Advance"); fin
|
||||
setCursor(cursX, cursY)
|
||||
|
||||
while TRUE
|
||||
pl->b_combatChoice = getUpperKey()
|
||||
|
@ -500,8 +549,8 @@ def playerCombatChoose(pl)#0
|
|||
return
|
||||
is 'S'
|
||||
if canShoot
|
||||
setCursor(cursX, cursY)
|
||||
chooseShotNumber(pl, pWeapon)
|
||||
setCursor(cursX, cursY)
|
||||
return
|
||||
fin
|
||||
break
|
||||
|
@ -510,8 +559,8 @@ def playerCombatChoose(pl)#0
|
|||
break
|
||||
is 'C'
|
||||
if canChange
|
||||
setCursor(cursX, cursY)
|
||||
chooseWeapon(pl, pWeapon)
|
||||
setCursor(cursX, cursY)
|
||||
return
|
||||
fin
|
||||
break
|
||||
|
@ -530,6 +579,14 @@ def playerCombatChoose(pl)#0
|
|||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Called after a single-use weapon is used (regardless of whether it hit or not)
|
||||
def checkSingleUse(pl, pWeapon)#0
|
||||
if !pWeapon; return; fin
|
||||
if !(pWeapon->b_flags & WEAPON_FLAG_SINGLE_USE); return; fin
|
||||
removeFromList(@pl=>p_items, pWeapon)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def playerCombatTurn(pl)#0
|
||||
word pWeapon, pGroup, pEnemy
|
||||
|
@ -548,7 +605,7 @@ def playerCombatTurn(pl)#0
|
|||
when pl->b_combatChoice
|
||||
is 'M'
|
||||
playerMelee(pl, pWeapon)
|
||||
// TODO: check for single-use weapon, consume it after use (even if it didn't hit)
|
||||
checkSingleUse(pl, pWeapon)
|
||||
break
|
||||
is 'F'
|
||||
break
|
||||
|
@ -557,22 +614,20 @@ def playerCombatTurn(pl)#0
|
|||
break
|
||||
is 'S'
|
||||
for i = 1 to pl->b_shotChoice
|
||||
if nEnemiesFighting
|
||||
if nEnemiesFighting and pWeapon->b_clipCurrent
|
||||
playerShoot(pl, pWeapon)
|
||||
consumeAmmo(pl, pWeapon)
|
||||
if i+1 < pl->b_shotChoice; combatPause; fin
|
||||
fin
|
||||
next
|
||||
// TODO: check for single-use weapon, consume it after use (even if it didn't hit)
|
||||
checkSingleUse(pl, pWeapon)
|
||||
break
|
||||
is 'R'
|
||||
pWeapon->b_clipCurrent = pWeapon->b_clipSize
|
||||
// TODO: Consume ammo here
|
||||
setPlural(FALSE)
|
||||
displayf1("\n%s reloads.\n", pl=>s_name)
|
||||
reload(pl, pWeapon, TRUE) // TRUE = echo
|
||||
break
|
||||
is 'C'
|
||||
// Choose a different weapon
|
||||
displayf2("\n%s switches to %s.\n", pl=>s_name, pWeapon=>s_name)
|
||||
displayf3("\n%s switches to %s %s.\n", pl=>s_name, hisHerTheir(pl->c_gender), pWeapon=>s_name)
|
||||
break
|
||||
is 'A'
|
||||
// Advance 5 feet
|
||||
|
@ -607,7 +662,7 @@ def enemyCombatTurn(pe)#1
|
|||
displayf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name)
|
||||
|
||||
// Roll to hit
|
||||
roll = rand16() % 100
|
||||
roll = rollPercentileWithLuck(pl->b_luck) // player luck can raise roll, reducing enemy chance to hit
|
||||
if combatDebug; displayf2("\nenemy hit roll=%d, need < %d\n", roll, pe->b_chanceToHit); fin
|
||||
needShow = FALSE
|
||||
if roll < pe->b_chanceToHit
|
||||
|
@ -634,15 +689,12 @@ def enemyCombatTurn(pe)#1
|
|||
|
||||
// Max dodge chance is 90%, min is 0.
|
||||
dodgeChance = max(0, min(90, dodgeChance))
|
||||
|
||||
// TODO: factor in luck
|
||||
roll = rand16() % 100
|
||||
roll = rollPercentileWithLuck(-(pl->b_luck)) // negate so luck has chance of reducing roll = better
|
||||
if combatDebug; displayf2("dodge roll %d, need < %d\n", roll, dodgeChance); fin
|
||||
if roll < dodgeChance
|
||||
displayf1("but %s dodges!", pl=>s_name)
|
||||
else
|
||||
// TODO: factor in luck
|
||||
dam = rollDice(pe=>r_enemyDmg)
|
||||
dam = rollDiceWithLuck(pe=>r_enemyDmg, -(pl->b_luck)) // player luck can reduce damage inflicted by enemy
|
||||
if combatDebug; displayf1("base dmg %d\n", dam); fin
|
||||
// Each point of armor-value reduces damage 2%, up to max of 90%
|
||||
damReduce = max(0, min(90, pl->b_armor * 2))
|
||||
|
@ -765,9 +817,8 @@ def collectLootAndXP()#2
|
|||
while group
|
||||
enemies = group=>p_enemies
|
||||
while enemies
|
||||
// TODO: factor in luck
|
||||
gold = gold + rollDice(enemies=>r_goldLoot)
|
||||
xp = xp + rollDice(enemies=>r_enemyXP)
|
||||
gold = gold + rollDiceWithLuck(enemies=>r_goldLoot, global=>p_players->b_luck)
|
||||
xp = xp + rollDiceWithLuck(enemies=>r_enemyXP, global=>p_players->b_luck)
|
||||
enemies = enemies=>p_nextObj
|
||||
loop
|
||||
group = group=>p_nextObj
|
||||
|
|
|
@ -60,6 +60,7 @@ import gamelib
|
|||
predef getYN()#1
|
||||
predef girdPlayer(player)#0
|
||||
predef giveItemToPlayer(p_player, itemFuncNum)#0
|
||||
predef hisHerTheir(c_gender)#1
|
||||
predef initHeap(loadedSize)#0
|
||||
predef initPlayerXP(player)#0
|
||||
predef loadFrameImg(img)#0
|
||||
|
@ -98,6 +99,8 @@ import gamelib
|
|||
predef rightJustifyNum(num, rightX)#0
|
||||
predef rightJustifyStr(str, rightX)#0
|
||||
predef rollDice(encoded)#1
|
||||
predef rollDiceWithLuck(encoded, luck)#1
|
||||
predef rollPercentileWithLuck(luck)#1
|
||||
predef scanForNamedObj(p_obj, name)#1
|
||||
predef scriptCombat(mapCode)#1
|
||||
predef scriptDisplayStr(str)#0
|
||||
|
@ -153,4 +156,7 @@ import gamelib
|
|||
|
||||
// Next: common events
|
||||
byte[] S_ENTER, S_LEAVE, S_USE
|
||||
|
||||
// Next: other useful strings
|
||||
byte[] S_HIS, S_HER, S_THEIR
|
||||
end
|
||||
|
|
|
@ -140,6 +140,9 @@ export byte[] S_SP = "SP"
|
|||
export byte[] S_ENTER = "Enter"
|
||||
export byte[] S_LEAVE = "Leave"
|
||||
export byte[] S_USE = "Use"
|
||||
export byte[] S_HIS = "his"
|
||||
export byte[] S_HER = "her"
|
||||
export byte[] S_THEIR = "their"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Definitions used by assembly code
|
||||
|
@ -1294,19 +1297,50 @@ export def encodeDice(nDice, dieSize, add)#1 // ndice=0..15, dieSize=0..15, add
|
|||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def rollDice(encoded)#1
|
||||
byte i, nDice, dieSize, add, result
|
||||
export def rollDiceWithLuck(encoded, luck)#1
|
||||
byte i, nDice, dieSize, add, droll, result
|
||||
nDice = encoded >> 12
|
||||
dieSize = (encoded >> 8) & $F
|
||||
add = encoded & $F
|
||||
result = add
|
||||
for i = 1 to nDice
|
||||
droll = (rand16() % dieSize) + 1
|
||||
if luck > 0
|
||||
if (rand16() % 100) < (luck * 2)
|
||||
droll = max(droll, (rand16() % dieSize) + 1)
|
||||
fin
|
||||
elsif luck < 0
|
||||
if (rand16() % 100) < (luck * -2)
|
||||
droll = min(droll, (rand16() % dieSize) + 1)
|
||||
fin
|
||||
fin
|
||||
add = (rand16() % dieSize) + 1
|
||||
result = result + add
|
||||
next
|
||||
return result
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def rollDice(encoded)#1
|
||||
return rollDiceWithLuck(encoded, 0)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
export def rollPercentileWithLuck(luck)#1
|
||||
byte result
|
||||
result = rand16() % 100
|
||||
if luck > 0
|
||||
if (rand16() % 100) < (luck * 2)
|
||||
result = max(result, rand16() % 100)
|
||||
fin
|
||||
elsif luck < 0
|
||||
if (rand16() % 100) < (luck * -2)
|
||||
result = min(result, rand16() % 100)
|
||||
fin
|
||||
fin
|
||||
return result
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Look up the partition for a resource.
|
||||
// sectioNum: 1=map2d, 2=map3d, 3=portrait
|
||||
|
@ -2856,6 +2890,14 @@ export def getStat(player, statName)#1
|
|||
puts(statName); return fatal("Unknown stat")
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Return an appropriate pronoun for the given gender (M/F/N...)
|
||||
export def hisHerTheir(gender)#1
|
||||
if gender == 'M'; return @S_HIS; fin
|
||||
if gender == 'F'; return @S_HER; fin
|
||||
return @S_THEIR
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def clampByte(val)#1
|
||||
return max(0, min(255, val))
|
||||
|
|
|
@ -124,7 +124,6 @@ end
|
|||
const ITEM_FLAG_EQUIP = $80 // only one weapon/armor equipped (in use) at a time
|
||||
|
||||
const WEAPON_FLAG_SINGLE_USE = $01
|
||||
const WEAPON_FLAG_WHOLE_GROUP = $02
|
||||
|
||||
const TYPE_WEAPON = $85
|
||||
struc Weapon
|
||||
|
|
Loading…
Reference in New Issue
Block a user