mirror of
https://github.com/badvision/lawless-legends.git
synced 2025-01-23 05:29:50 +00:00
Added partial support for NPC party members; packer now produces headers for the PLASMA code so we don't have to hard-code image numbers.
This commit is contained in:
parent
90187c007c
commit
be7ba4a3ed
@ -1356,16 +1356,52 @@ class PackPartitions
|
||||
println "Done."
|
||||
}
|
||||
|
||||
def humanNameToFuncName(str)
|
||||
def isAlnum(ch)
|
||||
{
|
||||
if (Character.isAlphabetic(ch.charAt(0) as int))
|
||||
return true
|
||||
if (Character.isDigit(ch.charAt(0) as int))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
def humanNameToSymbol(str, allUpper)
|
||||
{
|
||||
def buf = new StringBuilder()
|
||||
def inParen = false
|
||||
def inSlash = false
|
||||
str.each { ch ->
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
buf.append(ch)
|
||||
else if (ch >= 'a' && ch <= 'z')
|
||||
buf.append(ch)
|
||||
def needUnderscore = false
|
||||
str.eachWithIndex { ch, idx ->
|
||||
if (ch == '(') {
|
||||
inParen = true
|
||||
ch = 0
|
||||
}
|
||||
else if (ch == ')') {
|
||||
inParen = false
|
||||
ch = 0
|
||||
}
|
||||
else if (ch == '/') {
|
||||
inSlash = true
|
||||
ch = 0
|
||||
}
|
||||
else if (!isAlnum(ch))
|
||||
inSlash = false
|
||||
|
||||
if (ch && !inParen && !inSlash) {
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
needUnderscore = true
|
||||
if (ch == ' ' || ch == '_')
|
||||
needUnderscore = true
|
||||
if (isAlnum(ch)) {
|
||||
if (needUnderscore && idx > 0)
|
||||
buf.append('_')
|
||||
needUnderscore = false
|
||||
if (allUpper)
|
||||
buf.append(ch.toUpperCase())
|
||||
else
|
||||
buf.append(ch.toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString()
|
||||
}
|
||||
@ -1375,7 +1411,7 @@ class PackPartitions
|
||||
assert columns[0] == "Name"
|
||||
def name = data[0]
|
||||
|
||||
out.print("def ${humanNameToFuncName(name)}\n")
|
||||
out.print("def new_enemy_${humanNameToSymbol(name, false)}\n")
|
||||
|
||||
assert columns[1] == "Image1"
|
||||
def image1 = data[1]
|
||||
@ -1419,11 +1455,27 @@ class PackPartitions
|
||||
assert columns[14].toLowerCase() =~ /gold loot/
|
||||
def goldLoot = data[14]
|
||||
|
||||
|
||||
out.println("end\n")
|
||||
}
|
||||
|
||||
void dataGen()
|
||||
void dataGen(xmlPath)
|
||||
{
|
||||
// Open the XML data file produced by Outlaw Editor
|
||||
def dataIn = new XmlParser().parse(xmlPath)
|
||||
|
||||
// Translate image names to constants
|
||||
new File("src/plasma/gen_images.plh").withWriter { out ->
|
||||
def portraitNum = 0
|
||||
dataIn.image.each { image ->
|
||||
def category = image.@category?.toLowerCase()
|
||||
def name = image.@name
|
||||
if (category == "portrait") {
|
||||
++portraitNum
|
||||
out.println "const PORTRAIT_${humanNameToSymbol(name, true)} = $portraitNum"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translate enemies to code
|
||||
new File("src/plasma/gen_enemies.pla").withWriter { out ->
|
||||
def columns
|
||||
@ -1455,10 +1507,10 @@ class PackPartitions
|
||||
}
|
||||
|
||||
// Check the arguments
|
||||
if (!((args.size() == 1 && args[0] == "-dataGen") || args.size() == 2 || args.size() == 3)) {
|
||||
if (!(args.size() == 2 || args.size() == 3)) {
|
||||
println "Usage: convert yourOutlawFile.xml game.part.0.bin [intcastMap.js]"
|
||||
println " (where intcastMap.js is to aid in debugging the Javascript raycaster)"
|
||||
println " or: convert -dataGen"
|
||||
println " or: convert yourOutlawFile.xml -dataGen"
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@ -1470,8 +1522,8 @@ class PackPartitions
|
||||
// Go for it.
|
||||
def inst = new PackPartitions()
|
||||
try {
|
||||
if (args[0] == "-dataGen")
|
||||
inst.dataGen()
|
||||
if (args[1] == "-dataGen")
|
||||
inst.dataGen(args[0])
|
||||
else
|
||||
inst.pack(args[0], args[1], args.size() > 2 ? args[2] : null)
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
<!-- Generate code from tables -->
|
||||
<echo>Generating code from tables.</echo>
|
||||
<java jar="${pack.dir}/PackPartitions.jar" fork="true" failonerror="true">
|
||||
<arg value="data/world/world.xml"/>
|
||||
<arg value="-dataGen"/>
|
||||
</java>
|
||||
|
||||
|
@ -76,6 +76,7 @@ const CHAR_WND_LIFE_X = 91
|
||||
const CHAR_WND_GUN_X = 114
|
||||
|
||||
include "playtype.plh"
|
||||
include "gen_images.plh"
|
||||
//include "heaptest.plh"
|
||||
|
||||
word global // the global heap object, from which all live objects must be reachable
|
||||
@ -647,6 +648,15 @@ asm brk
|
||||
end
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reboot the machine
|
||||
// Params: None
|
||||
asm reboot
|
||||
inc $3F4 ; invalidate reset vector
|
||||
jmp $FA62 ; and reset
|
||||
end
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Set up the font engine
|
||||
// Params: pFont
|
||||
@ -1420,11 +1430,26 @@ def kbdLoop()
|
||||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def showMapName(mapName)
|
||||
word newNameHash
|
||||
|
||||
newNameHash = hashString(mapName)
|
||||
if newNameHash <> mapNameHash
|
||||
setWindow1()
|
||||
clearWindow()
|
||||
displayChar('Y'-$40) // center mode
|
||||
displayStr(mapName)
|
||||
displayChar('N'-$40) // normal mode
|
||||
if mapIs3D; copyWindow(); fin
|
||||
mapNameHash = newNameHash
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Set initial info for the scripts on this map: the name of the map, its trigger table, and the
|
||||
// maximum extent (width, height). This is called by the init function for the scripts.
|
||||
def setScriptInfo(mapName, trigTbl, wdt, hgt)
|
||||
word newNameHash
|
||||
|
||||
// Grab the trigger table origins (used so the table can be more compact)
|
||||
triggerOriginX = trigTbl=>0
|
||||
@ -1438,16 +1463,7 @@ def setScriptInfo(mapName, trigTbl, wdt, hgt)
|
||||
totalMapHeight = hgt
|
||||
|
||||
// Display map name
|
||||
newNameHash = hashString(mapName)
|
||||
if newNameHash <> mapNameHash
|
||||
setWindow1()
|
||||
clearWindow()
|
||||
displayChar('Y'-$40) // center mode
|
||||
displayStr(mapName)
|
||||
displayChar('N'-$40) // normal mode
|
||||
if mapIs3D; copyWindow(); fin
|
||||
mapNameHash = newNameHash
|
||||
fin
|
||||
showMapName(mapName)
|
||||
|
||||
// Back to the main text window.
|
||||
setWindow2()
|
||||
@ -1576,6 +1592,10 @@ end
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Call like this: addToList(player + items, itemToAdd)
|
||||
def addToList(addTo, p)
|
||||
// Get to the end of the list
|
||||
while *addTo
|
||||
addTo = (*addTo) + p_nextObj
|
||||
loop
|
||||
p=>p_nextObj = *addTo
|
||||
*addTo = p
|
||||
end
|
||||
@ -1704,7 +1724,7 @@ end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def playerDodge(pPlayer)
|
||||
displayStr("\nTODO: player dodge.\n")
|
||||
displayStr("\n%s dodges.\n", pPlayer=>s_name)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1760,10 +1780,6 @@ def playerCombatChoose(pl)
|
||||
byte nWeapons, key
|
||||
byte canShoot, canReload, canChange
|
||||
|
||||
// Tell what the player currently faces
|
||||
displayOpponents()
|
||||
combatPause()
|
||||
|
||||
// Count weapons, and take the first as the current one.
|
||||
nWeapons = 0
|
||||
pWeapon = NULL
|
||||
@ -1776,6 +1792,16 @@ def playerCombatChoose(pl)
|
||||
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
|
||||
@ -1934,7 +1960,9 @@ def determineCombatOrder()
|
||||
if canFight(p)
|
||||
p->b_combatOrder = rand16() % (p->b_agility * 10)
|
||||
combatInsert(p)
|
||||
nPlayersFighting = nPlayersFighting + 1
|
||||
if (!(p=>b_playerFlags & PLAYER_FLAG_NPC)) // only count real players
|
||||
nPlayersFighting = nPlayersFighting + 1
|
||||
fin
|
||||
fin
|
||||
p = p=>p_nextObj
|
||||
loop
|
||||
@ -1964,7 +1992,14 @@ def startCombat()
|
||||
|
||||
// Create the enemy group(s).
|
||||
global=>p_enemyGroups = NULL
|
||||
addToList(global + p_enemyGroups, new_EnemyGroup_Dirt_Bags())
|
||||
when rand16() % 2
|
||||
is 0
|
||||
addToList(global + p_enemyGroups, new_EnemyGroup_Dirt_Bags())
|
||||
break
|
||||
otherwise
|
||||
addToList(global + p_enemyGroups, new_EnemyGroup_Flesh_Feeders())
|
||||
break
|
||||
wend
|
||||
|
||||
// Display portrait of first group
|
||||
setPortrait(global=>p_enemyGroups=>p_enemies->ba_images[0])
|
||||
@ -2011,15 +2046,15 @@ def startCombat()
|
||||
loop
|
||||
|
||||
rawDisplayStr("\nDo you:\n")
|
||||
displayOption('F', "Fight")
|
||||
displayOption('R', "Run")
|
||||
displayOption('B', "Battle")
|
||||
displayOption('F', "Flee")
|
||||
while TRUE
|
||||
n = getUpperKey()
|
||||
if n == 'F'
|
||||
if n == 'B'
|
||||
clearWindow()
|
||||
displayStr("Fight!\n")
|
||||
displayStr("Battle!\n")
|
||||
return TRUE
|
||||
elsif n == 'R'
|
||||
elsif n == 'F'
|
||||
clearWindow()
|
||||
displayStr("Coward.")
|
||||
return FALSE
|
||||
@ -2051,18 +2086,29 @@ def doCombat()
|
||||
p = global=>p_combatFirst
|
||||
while p
|
||||
if !nPlayersFighting
|
||||
displayStr("\nYou lost.")
|
||||
//Lose: You bought the farm, with your life! Thank's for playing! Relead last save ? Y/N
|
||||
//Lose: Didn'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? Y/N
|
||||
return
|
||||
setPortrait(PORTRAIT_DEATH)
|
||||
when rand16() % 2
|
||||
is 0
|
||||
displayStr("\nYou bought the farm, with your life! Thank's 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
|
||||
if (!getYN())
|
||||
displayStr("Ah, okay. Well... just hit a key when you're ready then.")
|
||||
getUpperKey()
|
||||
fin
|
||||
reboot()
|
||||
elsif !nEnemiesFighting
|
||||
displayStr("\nYou won!")
|
||||
//WIN: You survive the ordeal and rifle through the carnage to find X gold. (if player gets random item) Upon further searching you find a
|
||||
//Win: That was a close call! You see something shimmering on the ground and find X gold.
|
||||
//Win: Looks like you live to fight another day anyway! You get X experience and discover X gold.
|
||||
//I forgot to add experience to the above ones
|
||||
// WIN: You survive the ordeal and rifle through the carnage to find X gold and receive x experience. (if player gets random item) Upon further searching you find a
|
||||
//Win: That was a close call! You see something shimmering on the ground and find X gold and x expereince.
|
||||
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.")
|
||||
@ -2085,6 +2131,55 @@ def doCombat()
|
||||
loop
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Show player data
|
||||
def showPlayerSheet(pl)
|
||||
word x, y
|
||||
byte dir
|
||||
getPos(@x, @y)
|
||||
dir = getDir()
|
||||
|
||||
// First, display the player's name in the title bar
|
||||
showMapName(pl=>s_name)
|
||||
|
||||
// Next, show stats in the main map area
|
||||
setMapWindow()
|
||||
clearWindow()
|
||||
displayStr("Stats")
|
||||
|
||||
// Show inventory in the right hand area
|
||||
setWindow2()
|
||||
clearWindow()
|
||||
displayStr("Inventory")
|
||||
|
||||
// Wait, then back to normal
|
||||
getUpperKey()
|
||||
|
||||
setWindow2()
|
||||
clearWindow()
|
||||
initMap(x, y, dir)
|
||||
end
|
||||
|
||||
def showPlayer1()
|
||||
showPlayerSheet(global=>p_players)
|
||||
end
|
||||
|
||||
def showPlayer2()
|
||||
if global=>p_players=>p_nextObj
|
||||
showPlayerSheet(global=>p_players=>p_nextObj)
|
||||
else
|
||||
beep()
|
||||
fin
|
||||
end
|
||||
|
||||
def showPlayer3()
|
||||
if global=>p_players=>p_nextObj and global=>p_players=>p_nextObj=>p_nextObj
|
||||
showPlayerSheet(global=>p_players=>p_nextObj=>p_nextObj)
|
||||
else
|
||||
beep()
|
||||
fin
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Set up the command table for 3D mode
|
||||
def initCmds()
|
||||
@ -2100,6 +2195,9 @@ def initCmds()
|
||||
initCmd('P', @showPos)
|
||||
initCmd('/', @testPortrait)
|
||||
initCmd('!', @doCombat)
|
||||
initCmd('1', @showPlayer1)
|
||||
initCmd('2', @showPlayer2)
|
||||
initCmd('3', @showPlayer3)
|
||||
|
||||
// Commands handled differently in 3D vs 2D
|
||||
if mapIs3D
|
||||
@ -2238,6 +2336,7 @@ end
|
||||
//
|
||||
initHeap()
|
||||
addToList(global + p_players, new_Player_Hue_Hauser())
|
||||
addToList(global + p_players, new_Player_Mokahnu())
|
||||
loadTitle()
|
||||
setCallbacks()
|
||||
// Start map/loc per Seth. Need to have this in a script in the futurecheckScripts()
|
||||
|
@ -35,6 +35,42 @@ def new_Armor_Chaps
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Armor_ShamanHeaddress()
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ARMOR)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Shaman Headdress(es)")
|
||||
p->b_itemKind = KIND_HAT
|
||||
p=>w_cost = -99 // for now
|
||||
// no modifiers, max uses, etc. for now
|
||||
p->b_armorValue = 2
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Armor_TahnkuPants()
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ARMOR)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Tahnku Pants")
|
||||
p->b_itemKind = KIND_PANTS
|
||||
p=>w_cost = -99 // for now
|
||||
// no modifiers, max uses, etc. for now
|
||||
p->b_armorValue = 2
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Armor_TahnkuVest()
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ARMOR)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Tahnku Vest(s)")
|
||||
p->b_itemKind = KIND_SHIRT
|
||||
p=>w_cost = -99 // for now
|
||||
// no modifiers, max uses, etc. for now
|
||||
p->b_armorValue = 2
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def encodeDice(nDice, dieSize, add) // ndice=0..15, dieSize=0..15, add=0..255
|
||||
return (nDice << 12) | (dieSize << 8) | add
|
||||
@ -73,6 +109,45 @@ def new_Weapon_Handgun
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Weapon_SpiritBow
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_WEAPON)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Spirit Bow")
|
||||
p->b_itemKind = KIND_BOW
|
||||
p=>w_cost = -99 // for now
|
||||
// no modifiers, max uses, etc. for now
|
||||
p->b_ammoKind = KIND_ARROW
|
||||
p->b_clipSize = 12
|
||||
p->b_clipCurrent = p->b_clipSize
|
||||
p=>r_meleeDmg = encodeDice(3, 6, 0) // 3d6
|
||||
p=>r_projectileDmg = encodeDice(2, 6, 5) // 2d6+5
|
||||
p->ba_attacks[0] = 1 // single attack
|
||||
p->ba_attacks[1] = 3 // triple attack
|
||||
p->b_weaponRange = 100
|
||||
p=>s_combatText = mmgr(HEAP_INTERN, "shoots")
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Weapon_SpiritBlade
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_WEAPON)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Spirit Blade")
|
||||
p->b_itemKind = KIND_BOW
|
||||
p=>w_cost = -99 // for now
|
||||
// no modifiers, max uses, etc. for now
|
||||
p->b_ammoKind = 0
|
||||
p->b_clipSize = 0
|
||||
p->b_clipCurrent = p->b_clipSize
|
||||
p=>r_meleeDmg = encodeDice(7, 6, 0) // 7d6
|
||||
p=>r_projectileDmg = 0
|
||||
p->ba_attacks[0] = 1 // single attack
|
||||
p->b_weaponRange = 10
|
||||
p=>s_combatText = mmgr(HEAP_INTERN, "slices")
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def calcPlayerArmor(player)
|
||||
word pItem
|
||||
@ -126,13 +201,61 @@ def new_Player_Hue_Hauser
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Player_Mokahnu
|
||||
word p, pItem
|
||||
p = mmgr(HEAP_ALLOC, TYPE_PLAYER)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Mokahnu")
|
||||
p->b_intelligence = 7
|
||||
p->b_strength = 4
|
||||
p->b_agility = 6
|
||||
p->b_bravery = 7
|
||||
p->b_stamina = 6
|
||||
p->b_charisma = 7
|
||||
p->b_spirit = 10
|
||||
|
||||
p=>w_maxHealth = 40
|
||||
p=>w_health = 40
|
||||
|
||||
p->b_playerFlags = PLAYER_FLAG_NPC
|
||||
|
||||
// Basic skills
|
||||
p->b_aiming = 4
|
||||
p->b_dodging = 3
|
||||
p->b_wilderness = 5
|
||||
|
||||
// Skills
|
||||
addToList(p + p_skills, new_Modifier(KIND_MINING, 0))
|
||||
addToList(p + p_skills, new_Modifier(KIND_NATIVE_BOND, 10))
|
||||
addToList(p + p_skills, new_Modifier(KIND_PYRE_WARE, 0))
|
||||
addToList(p + p_skills, new_Modifier(KIND_BLADE, 3))
|
||||
addToList(p + p_skills, new_Modifier(KIND_BOW, 3))
|
||||
addToList(p + p_skills, new_Modifier(KIND_RIFLE, 3))
|
||||
addToList(p + p_skills, new_Modifier(KIND_BLADE, 3))
|
||||
|
||||
// Items
|
||||
addToList(p + p_items, new_Armor_ShamanHeaddress())
|
||||
addToList(p + p_items, new_Armor_TahnkuPants())
|
||||
addToList(p + p_items, new_Armor_TahnkuVest())
|
||||
addToList(p + p_items, new_Weapon_SpiritBow())
|
||||
addToList(p + p_items, new_Weapon_SpiritBlade())
|
||||
|
||||
// Calculated attributes
|
||||
calcPlayerArmor(p)
|
||||
|
||||
// (No buffs or debuffs to start with.)
|
||||
|
||||
// All done with the player.
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Enemy_Dirt_Bag
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ENEMY)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Dirt-Bag(s)")
|
||||
p=>w_health = rollDice(encodeDice(1, 6, 0))
|
||||
p->ba_images[0] = 32 // Gunman5
|
||||
p->ba_images[0] = PORTRAIT_GUNMAN5
|
||||
p->b_attackType = 1 // melee
|
||||
p=>s_attackText = mmgr(HEAP_INTERN, "swings at")
|
||||
p->b_enemyAttackRange = 5
|
||||
@ -160,3 +283,37 @@ def new_EnemyGroup_Dirt_Bags
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_Enemy_Flesh_Feeder
|
||||
word p
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ENEMY)
|
||||
p=>s_name = mmgr(HEAP_INTERN, "Flesh Feeder(s)")
|
||||
p=>w_health = rollDice(encodeDice(6, 6, 0))
|
||||
p->ba_images[0] = PORTRAIT_U_D_MAN1
|
||||
p->b_attackType = 1 // melee
|
||||
p=>s_attackText = mmgr(HEAP_INTERN, "bites")
|
||||
p->b_enemyAttackRange = 5
|
||||
p->b_chanceToHit = 40
|
||||
p=>r_enemyDmg = encodeDice(3, 6, 0) // 3d6
|
||||
p=>r_groupSize = encodeDice(1, 6, 0) // 1d6
|
||||
p=>r_initialRange = encodeDice(1, 5, 0)
|
||||
return p
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
def new_EnemyGroup_Flesh_Feeders
|
||||
word p, enem, groupSize
|
||||
p = mmgr(HEAP_ALLOC, TYPE_ENEMY_GROUP)
|
||||
enem = new_Enemy_Flesh_Feeder()
|
||||
p->b_enemyGroupRange = rollDice(enem=>r_initialRange)
|
||||
|
||||
groupSize = rollDice(enem=>r_groupSize)
|
||||
addToList(p + p_enemies, enem)
|
||||
while groupSize > 1
|
||||
addToList(p + p_enemies, new_Enemy_Flesh_Feeder())
|
||||
groupSize = groupSize - 1
|
||||
loop
|
||||
|
||||
return p
|
||||
end
|
||||
|
||||
|
@ -10,6 +10,8 @@ struc Global
|
||||
end
|
||||
byte typeTbl_Global[] = Global, p_players, p_enemyGroups, p_combatFirst, 0
|
||||
|
||||
const PLAYER_FLAG_NPC = $01
|
||||
|
||||
const TYPE_PLAYER = $81
|
||||
struc Player
|
||||
byte t_type
|
||||
@ -39,6 +41,7 @@ struc Player
|
||||
// Status
|
||||
word w_maxHealth
|
||||
byte b_combatChoice
|
||||
byte b_playerFlags
|
||||
|
||||
// Lists
|
||||
word p_skills // list:Modifier
|
||||
|
Loading…
x
Reference in New Issue
Block a user