Forgot to check in some files from the combat refactor.

This commit is contained in:
Martin Haye 2015-12-11 06:48:00 -08:00
parent 54bdfdd47e
commit 70174a3342
3 changed files with 642 additions and 0 deletions

4
.gitignore vendored
View File

@ -20,6 +20,7 @@ mg
/Platform/Apple/virtual/data/images/*.bin
/Platform/Apple/virtual/data/fonts/*.bin
/Platform/Apple/virtual/data/world/*.xml
/Platform/Apple/virtual/data/world/*.tsv
# Only check in sample.build.props; each person's build.props will be different.
/Platform/Apple/virtual/src/include/build.props
@ -34,5 +35,8 @@ mg
/Platform/Apple/tools/PLASMA/src/PLVM02.SYSTEM.sys
/Platform/Apple/tools/PLASMA/src/*#*
# Ignore other auto-generated files
/Platform/Apple/virtual/src/plasma/gen_*.pl?
# Packer sometimes produces an error text file.
error_stack.txt

View File

@ -0,0 +1,526 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
// (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
// Unless required by applicable law or agreed to in writing, software distributed under
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the License for the specific language
// governing permissions and limitations under the License.
///////////////////////////////////////////////////////////////////////////////////////////////////
include "gamelib.plh"
include "playtype.plh"
include "gen_images.plh"
word global
// Combat variables that don't need to be saved in game state
byte nPlayersFighting
byte nEnemiesFighting
byte isFleeing
///////////////////////////////////////////////////////////////////////////////////////////////////
def canFight(p)
return p=>w_health > 0
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def chooseEnemy(maxDist)
word p
byte n
// TODO: Consider distance of enemies
n = rand16() % nEnemiesFighting
p = global=>p_combatFirst
while TRUE
if p->t_type == TYPE_ENEMY and canFight(p)
if n == 0; return p; fin
n = n - 1
fin
p = p=>p_combatNext
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def playerMelee(pPlayer, pWeapon)
word pEnemy, dmg
pEnemy = chooseEnemy(5) // max distance 5 feet for melee
if !pEnemy; return FALSE; fin
if pWeapon
dmg = rollDice(pWeapon=>r_meleeDmg)
else
dmg = (rand16() % 6) + 1 // default to 1d6 for fists
fin
// TODO: Add extra melee damage for skills, strength, etc.
// TODO: consider enemy dodge
pEnemy=>w_health = pEnemy=>w_health - dmg
isPlural = FALSE
buildString(@addToString)
printf3("\n%s pummels %s for %d damage.", pPlayer=>s_name, pEnemy=>s_name, dmg)
if pEnemy=>w_health <= 0
printf1(" %s is killed!", pEnemy=>s_name)
nEnemiesFighting = nEnemiesFighting - 1
fin
puts("\n")
displayStr(finishString(0))
return TRUE
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def playerShoot(pPlayer, pWeapon)
word pEnemy, pSkill, dmg
word chance
byte roll
pEnemy = chooseEnemy(pWeapon->b_weaponRange)
if !pEnemy; return FALSE; fin
// Figure out chance to hit. First come agility and aim.
chance = (pPlayer->b_agility * 4) + pPlayer->b_aiming
// Add in skill modifier, if any
pSkill = pPlayer=>p_skills
while pSkill
if pSkill=>b_modKind == pWeapon=>b_itemKind
chance = chance + pSkill=>b_modValue
fin
pSkill = pSkill=>p_nextObj
loop
// Nobody can have 100% chance to hit. Let's cap it at 90%.
chance = min(90, chance)
roll = rand16() % 100
if roll >= chance
// Miss!
displayf2("\n%s shoots at %s but misses.\n", pPlayer=>s_name, pEnemy=>s_name)
else
// TODO: consider multi-shot weapons
dmg = rollDice(pWeapon=>r_projectileDmg)
// TODO: Add extra melee damage for skills, strength, etc.
// TODO: consider enemy dodge
pEnemy=>w_health = pEnemy=>w_health - dmg
buildString(@addToString)
printf3("\n%s shoots %s for %d damage.", pPlayer=>s_name, pEnemy=>s_name, dmg)
if pEnemy=>w_health <= 0
printf1(" %s is killed!", pEnemy=>s_name)
nEnemiesFighting = nEnemiesFighting - 1
fin
puts("\n")
displayStr(finishString(0))
return TRUE
fin
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def playerDodge(pPlayer)
displayf1("\n%s dodges.\n", pPlayer=>s_name)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def chooseWeapon(player)
displayStr("\nTODO: choose weapon\n")
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def combatPause()
word n
for n = 1 to 3000
next
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayOpponents()
word p
byte count, first
buildString(@addToString)
puts("\nYou face ")
first = TRUE
p = global=>p_enemyGroups
while p
if !first
if p=>p_nextObj
puts(", ")
else
puts(" and ")
fin
fin
first = FALSE
count = countListFiltered(p=>p_enemies, p_nextObj, @canFight)
isPlural = (count <> 1)
printf3("%d %s at %d'", count, p=>p_enemies=>s_name, p->b_enemyGroupRange)
p = p=>p_nextObj
loop
puts(".\n")
displayStr(finishString(isPlural))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def displayOption(key, str)
buildString(@addToString)
printf2(" (%c)^T033%s\n", key, str)
rawDisplayStr(finishString(0))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def playerCombatChoose(pl)
word p, pWeapon
byte nWeapons, key
byte canShoot, canReload, canChange
// Count weapons, and take the first as the current one.
nWeapons = 0
pWeapon = NULL
p = pl=>p_items
while p
if p->t_type == TYPE_WEAPON
if !pWeapon; pWeapon = p; fin
nWeapons = nWeapons + 1
fin
p = p=>p_nextObj
loop
// NPCs always melee for now
if pl=>b_playerFlags & PLAYER_FLAG_NPC
pl->b_combatChoice = 'M'
return
fin
// Tell what the player currently faces
displayOpponents()
combatPause()
// Let them know their options
displayStr("\n")
when rand16() % 5
is 0
displayStr("Think fast!"); break
is 1
displayStr("Do you:"); break
is 2
displayStr("Suck it up:"); break
is 3
displayStr("Options:"); break
otherwise
displayStr("Decisions, decisions..."); break
wend
displayStr("\n")
displayOption('M', "Melee")
if pWeapon
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.
canReload = TRUE
displayOption('R', "Reload")
fin
if nWeapons >= 2
canChange = TRUE
displayOption('C', "Change weapon")
fin
fin
displayOption('D', "Dodge")
displayOption('F', "Flee")
while TRUE
pl->b_combatChoice = getUpperKey()
when pl->b_combatChoice
is 'M'
return
is 'F'
isFleeing = TRUE
return
is 'D'
return
is 'S'
if canShoot; return; fin
break
is 'R'
if canReload; return; fin
break
is 'C'
if canChange; return; fin
break
wend
beep()
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def playerCombatTurn(pl)
word pWeapon
// Get weapon
pWeapon = pl=>p_items
while pWeapon
if pWeapon->t_type == TYPE_WEAPON; break; fin
pWeapon = pWeapon=>p_nextObj
loop
// Execute the player's choice
when pl->b_combatChoice
is 'M'
playerMelee(pl, pWeapon)
break
is 'F'
break
is 'D'
playerDodge(pl)
break
is 'S'
playerShoot(pl, pWeapon)
break
is 'R'
pWeapon->b_clipCurrent = pWeapon->b_clipSize
displayf1("%s has reloaded.\n", pl=>s_name)
break
is 'C'
isPlural = FALSE
displayf2("%s changed to using %s.\n", pl=>s_name, pWeapon=>s_name)
break
wend
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def enemyCombatTurn(pe)
word pl
byte roll, dam
// Choose a target
pl = randomFromListFiltered(global=>p_players, p_nextObj, @canFight)
if !pl; return; fin
buildString(@addToString)
printf3("\n%s %s %s ", pe=>s_name, pe=>s_attackText, pl=>s_name)
// Roll to hit
roll = rand16() % 100
if roll <= pe->b_chanceToHit
dam = rollDice(pe=>r_enemyDmg)
printf1("and hits for %d damage!", dam)
pl=>w_health = max(0, pl=>w_health - dam)
if pl=>w_health == 0
printf1(" %s is killed!", pl=>s_name)
fin
showParty()
else
puts("and misses.")
fin
puts("\n")
displayStr(finishString(0))
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def combatInsert(toAdd)
word p, pPrev
// Find the proper position based on combat order number (keep largest first in the list)
pPrev = global + p_combatFirst
while TRUE
p = *pPrev
if !p or p->b_combatOrder < toAdd->b_combatOrder
break
fin
pPrev = p + p_combatNext
loop
// Add it right there.
toAdd=>p_combatNext = *pPrev
*pPrev = toAdd
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def determineCombatOrder()
word p, p2
nPlayersFighting = 0
nEnemiesFighting = 0
// Do each player character, with random chance based on their agility
global=>p_combatFirst = NULL
p = global=>p_players
while p
if canFight(p)
p->b_combatOrder = rand16() % (p->b_agility * 10)
combatInsert(p)
if (!(p=>b_playerFlags & PLAYER_FLAG_NPC)) // only count real players
nPlayersFighting = nPlayersFighting + 1
fin
fin
p = p=>p_nextObj
loop
// Then all the enemies, with random chance based on their chance to hit.
p = global=>p_enemyGroups
while p
p2 = p=>p_enemies
while p2
if canFight(p2)
p2->b_combatOrder = rand16() % p2->b_chanceToHit
combatInsert(p2)
nEnemiesFighting = nEnemiesFighting + 1
fin
p2 = p2=>p_nextObj
loop
p = p=>p_nextObj
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def startCombat()
word p, p2, n, s
// Setup
isFleeing = FALSE
// Display portrait of first group
setPortrait(global=>p_enemyGroups=>p_enemies->ba_images[0])
// We're going to do all our text drawing in window 2. Also, might as well
// set everything up so that the map gets redrawn when combat finishes.
setWindow2()
clearWindow()
textDrawn = TRUE
needRender = TRUE
// Say who we're fighting
when rand16() % 5
is 0
s = "Uh oh, it's gunna' be one of THOSE days.\n"; break
is 1
s = "Smells like trouble!\n"; break
is 2
s = "The hairs on the back of your neck reach for the sky!\n"; break
is 3
s = "You've got a bad feeling about this...\n"; break
otherwise
s = "Momma said there'd be days like this!\n"; break
wend
displayStr(s)
p = global=>p_enemyGroups
while p
n = countList(p=>p_enemies)
isPlural = (n <> 1)
when rand16() % 5
is 0
s = "From out of nowhere comes/come %d %s to have their way with you!\n"; break
is 1
s = "%d nasty %s comes/come to stake their claim on you!\n"; break
is 2
s = "It's gone from bad to worse, %d %s is/are looking for trouble!\n"; break
is 3
s = "%d %s is/are sniffing you out, shoulda' bathed last week!\n"; break
otherwise
s = "You mutter a curse under your breath as you see %d %s approach you with malice!\n"; break
wend
displayf2(s, n, p=>p_enemies=>s_name)
p = p=>p_nextObj
loop
rawDisplayStr("\nDo you:\n")
displayOption('B', "Battle")
displayOption('F', "Flee")
while TRUE
n = getUpperKey()
if n == 'B'
clearWindow()
displayStr("Battle!\n")
return TRUE
elsif n == 'F'
clearWindow()
displayStr("Coward.")
return FALSE
fin
beep()
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def doCombat()
word p
byte answer
// Show portrait and threat details, find out if player wants to fight (vs. run)
if !startCombat(); return; fin
// Do each round of combat until player wins, loses, or flees
while TRUE
p = global=>p_players
while p
if canFight(p)
playerCombatChoose(p)
fin
p = p=>p_nextObj
loop
determineCombatOrder()
p = global=>p_combatFirst
while p
if TRUE or !nPlayersFighting
setPortrait(PORTRAIT_DEATH)
when rand16() % 2
is 0
displayStr("\nYou bought the farm, with your life! Thanks for playing! Reload last save?\n"); break
otherwise
displayStr("\nDidn't see that coming... to see a fine player like you slaughtered like a common rodent... Well, let's reload and try that again, ok?\n")
wend
answer = getYN() // workaround for PLASMA problem: "if !getYN()" results in CALL 0
if !answer
displayStr("Ah, okay. Well... just hit a key when you're ready then.")
getUpperKey()
fin
reboot()
elsif !nEnemiesFighting
setPortrait(PORTRAIT_COMBATWIN)
when rand16() % 3
is 0
displayStr("\nYou survive the ordeal and rifle through the carnage.\n"); break
is 1
displayStr("\nThat was a close call! You see search the ground.\n"); break
otherwise
displayStr("\nLooks like you live to fight another day anyway!\n"); break
wend
getUpperKey()
return
elsif isFleeing
displayStr("\nYou have fled.")
return
fin
if canFight(p)
when p->t_type
is TYPE_PLAYER
playerCombatTurn(p); break
is TYPE_ENEMY
enemyCombatTurn(p); break
otherwise
brk()
wend
combatPause()
fin
p = p=>p_combatNext
loop
loop
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// initialization code
global = getGlobals()
doCombat()
done

View File

@ -0,0 +1,112 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
// (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
// Unless required by applicable law or agreed to in writing, software distributed under
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the License for the specific language
// governing permissions and limitations under the License.
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Handy constants.
const FALSE = 0
const TRUE = 1
const NULL = 0
// Library shared variables
const textDrawn = $3CD
const needRender = $3CE
const isPlural = $3CF
///////////////////////////////////////////////////////////////////////////////////////////////////
// Memory manager definitions
// Resource types
const RES_TYPE_CODE = 1
const RES_TYPE_2D_MAP = 2
const RES_TYPE_3D_MAP = 3
const RES_TYPE_TILE = 4
const RES_TYPE_TEXTURE = 5
const RES_TYPE_SCREEN = 6
const RES_TYPE_FONT = 7
const RES_TYPE_MODULE = 8
const RES_TYPE_BYTECODE = 9
const RES_TYPE_FIXUP = 10
const RES_TYPE_PORTRAIT = 11
// Command codes
const RESET_MEMORY = $10
const REQUEST_MEMORY = $11
const LOCK_MEMORY = $12
const UNLOCK_MEMORY = $13
const SET_MEM_TARGET = $14
const START_LOAD = $15
const QUEUE_LOAD = $16
const FINISH_LOAD = $17
const FREE_MEMORY = $18
const CALC_FREE = $19
const DEBUG_MEM = $1A
const CHECK_MEM = $1B
const CHAIN_LOADER = $1E
const FATAL_ERROR = $1F
const HEAP_SET = $20
const HEAP_ADD_TYPE = $21
const HEAP_ALLOC = $22
const HEAP_INTERN = $23
const HEAP_COLLECT = $24
///////////////////////////////////////////////////////////////////////////////////////////////////
// Shared library routines
const gameLibVecs = $1F00
const getGlobals = gameLibVecs + 3*0
const rand16 = gameLibVecs + 3*1
const printf1 = gameLibVecs + 3*2
const printf2 = gameLibVecs + 3*3
const printf3 = gameLibVecs + 3*4
const printf4 = gameLibVecs + 3*5
const displayf1 = gameLibVecs + 3*6
const displayf2 = gameLibVecs + 3*7
const displayf3 = gameLibVecs + 3*8
const displayf4 = gameLibVecs + 3*9
const buildString = gameLibVecs + 3*10
const addToString = gameLibVecs + 3*11
const finishString = gameLibVecs + 3*12
const rawDisplayStr = gameLibVecs + 3*13
const displayStr = gameLibVecs + 3*14
const puts = gameLibVecs + 3*15
const min = gameLibVecs + 3*16
const max = gameLibVecs + 3*17
const countList = gameLibVecs + 3*18
const countListFiltered = gameLibVecs + 3*19
const randomFromListFiltered = gameLibVecs + 3*20
const addToList = gameLibVecs + 3*21
const getUpperKey = gameLibVecs + 3*22
const beep = gameLibVecs + 3*23
const showParty = gameLibVecs + 3*24
const mmgr = gameLibVecs + 3*25
const setPortrait = gameLibVecs + 3*26
const setWindow1 = gameLibVecs + 3*27
const setWindow2 = gameLibVecs + 3*28
const setWindow3 = gameLibVecs + 3*29
const clearWindow = gameLibVecs + 3*30
const getYN = gameLibVecs + 3*31
const reboot = gameLibVecs + 3*32
const brk = gameLibVecs + 3*33
const encodeDice = gameLibVecs + 3*34
const rollDice = gameLibVecs + 3*35
const FUNCN36 = gameLibVecs + 3*36
const FUNCN37 = gameLibVecs + 3*37
const FUNCN38 = gameLibVecs + 3*38
const FUNCN39 = gameLibVecs + 3*39
const FUNCN40 = gameLibVecs + 3*40
const FUNCN41 = gameLibVecs + 3*41
const FUNCN42 = gameLibVecs + 3*42
const FUNCN43 = gameLibVecs + 3*43
const FUNCN44 = gameLibVecs + 3*44
const FUNCN45 = gameLibVecs + 3*45
const FUNCN46 = gameLibVecs + 3*46
const FUNCN47 = gameLibVecs + 3*47
const FUNCN48 = gameLibVecs + 3*48
const FUNCN49 = gameLibVecs + 3*49