Good progress on implementing encounter zones.

This commit is contained in:
Martin Haye 2016-03-29 15:17:42 -07:00
parent 510385ccd3
commit d17ef502e3
7 changed files with 158 additions and 25 deletions

View File

@ -67,6 +67,7 @@
<block type="events_move_backward"></block>
<block type="events_set_sky"></block>
<block type="events_set_ground"></block>
<block type="events_add_encounter_zone"></block>
</category>
<category name="Text">
<block type="text_window"></block>

View File

@ -369,6 +369,28 @@ if (typeof Mythos === "undefined") {
this.setTooltip('Set color of the ground');
}
};
Blockly.Blocks['events_add_encounter_zone'] = {
init: function () {
this.setHelpUrl(Mythos.helpUrl);
this.setColour(54);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.appendDummyInput()
.appendField("Add encounter zone for enemy code")
.appendField(new Blockly.FieldTextInput(""), "CODE")
.appendField('at X=')
.appendField(new Blockly.FieldTextInput("0"), "X")
.appendField('Y=')
.appendField(new Blockly.FieldTextInput("0"), "Y")
.appendField('with max dist')
.appendField(new Blockly.FieldTextInput("0"), "MAXDIST")
.appendField('(0=inf), and chance')
.appendField(new Blockly.FieldTextInput("0.0"), "CHANCE")
.appendField('%');
this.setOutput(false);
this.setTooltip('Add an encounter zone');
}
};
Blockly.Blocks['text_window'] = {
init: function () {
this.setHelpUrl(Mythos.helpUrl);

View File

@ -883,8 +883,10 @@ class PackPartitions
def name = mapEl.@name ?: "map$num"
def num = mapNames[name][1]
//println "Packing 2D map #$num named '$name'."
def rows = parseMap(mapEl, tileEls)
write2DMap(name, mapEl, rows)
withContext("map '$name'") {
def rows = parseMap(mapEl, tileEls)
write2DMap(name, mapEl, rows)
}
}
def pack3DMap(mapEl, tileEls)
@ -1769,7 +1771,8 @@ class PackPartitions
"${range.replace("'", "").toInteger()}, " +
"${chanceToHit.toInteger()}, " +
"${parseDice(damage)}, " +
"${parseDice(groupSize)})")
"${parseDice(groupSize)}, " +
"\"$mapCode\")")
out.println("end")
}
@ -2137,8 +2140,8 @@ class PackPartitions
(!yRange || trig.@y.toInteger() in yRange) })
{
scripts << script
scriptNames[script] = (name == null) ? "trig_$idx" : "sc_${humanNameToSymbol(name, false)}"
}
scriptNames[script] = (name == null) ? "trig_$idx" : "sc_${humanNameToSymbol(name, false)}"
}
// Even if there were no scripts, we still need an init to display
@ -2224,6 +2227,8 @@ class PackPartitions
packSetSky(blk); break
case 'events_set_ground':
packSetGround(blk); break
case 'events_add_encounter_zone':
packAddEncounterZone(blk); break
case 'events_teleport':
packTeleport(blk); break
case 'events_move_backward':
@ -2426,6 +2431,25 @@ class PackPartitions
outIndented("setGround($color)\n")
}
def packAddEncounterZone(blk)
{
assert blk.field.size() == 5
assert blk.field[0].@name == 'CODE'
assert blk.field[1].@name == 'X'
assert blk.field[2].@name == 'Y'
assert blk.field[3].@name == 'MAXDIST'
assert blk.field[4].@name == 'CHANCE'
def code = blk.field[0].text()
def x = blk.field[1].text().toInteger()
def y = blk.field[2].text().toInteger()
def maxDist = blk.field[3].text().toInteger()
def chance = (int)(blk.field[4].text().toFloat() * 10.0)
assert chance > 0 && chance <= 1000
outIndented("addEncounterZone(")
emitString(code)
out << ", $x, $y, $maxDist, $chance)\n"
}
def packTeleport(blk)
{
assert blk.field.size() == 3
@ -2495,13 +2519,11 @@ class PackPartitions
out << "byte = \$FF\n\n"
}
def makeInit(mapName, scripts, maxX, maxY)
def makeInit(mapName, script, maxX, maxY)
{
// Emit the code the user has stored for the init script.
scripts.each { script ->
if (script.block.size() == 1)
packBlock(getSingle(getSingle(script.block, null, 'procedures_defreturn').statement, 'STACK'))
}
// Emit the code the user has stored for the init script (if any)
if (script)
packScript(script)
// Set up the pointer to global vars
out << "global = getGlobals()\n"
@ -2510,6 +2532,10 @@ class PackPartitions
def shortName = mapName.replaceAll(/[\s-]*[23][dD][-0-9]*$/, '').take(16)
out << "setScriptInfo(\"$shortName\", @triggerTbl, $maxX, $maxY)\n"
// Call init script if one was defined
if (script)
out << "sc_${humanNameToSymbol(getScriptName(script), false)}()\n"
// All done with the init function.
out << "done\n"
}

View File

@ -13,6 +13,8 @@
const FALSE = 0
const TRUE = 1
const NULL = 0
const INT_MAX = 32767
const INT_MIN = -32768
///////////////////////////////////////////////////////////////////////////////////////////////////
// Memory manager definitions
@ -102,7 +104,7 @@ const setPlural = gameLibVecs + 3*43
const makeEnemy = gameLibVecs + 3*44
const getStringResponse = gameLibVecs + 3*45
const strcmpi = gameLibVecs + 3*46
const FUNCN47 = gameLibVecs + 3*47
const addEncounterZone = gameLibVecs + 3*47
const FUNCN48 = gameLibVecs + 3*48
const FUNCN49 = gameLibVecs + 3*49
const FUNCN50 = gameLibVecs + 3*50

View File

@ -106,7 +106,7 @@ predef _puts, _min, _max
predef _countList, _countListFiltered, _randomFromListFiltered, _addToList, _beep
predef _showParty, _mmgr, _setWindow1, _setWindow2, _setWindow3
predef _reboot, _brk, _encodeDice, _rollDice
predef _setPlural, _makeEnemy, _getStringResponse, _strcmpi
predef _setPlural, _makeEnemy, _getStringResponse, _strcmpi, _addEncounterZone
word gameLib_addrs = @_setScriptInfo, @_scriptDisplayStr, @_scriptDisplayStrNL, @_getYN
word = @_queue_setMap, @_setSky, @_setGround, @_queue_teleport, @_setPortrait, @_clearPortrait
@ -119,7 +119,7 @@ word = @_puts, @_min, @_max
word = @_countList, @_countListFiltered, @_randomFromListFiltered, @_addToList, @_beep
word = @_showParty, @_mmgr, @_setWindow1, @_setWindow2, @_setWindow3
word = @_reboot, @_brk, @_encodeDice, @_rollDice
word = @_setPlural, @_makeEnemy, @_getStringResponse, @_strcmpi
word = @_setPlural, @_makeEnemy, @_getStringResponse, @_strcmpi, @_addEncounterZone
word = 0 // end of library functions
@ -818,6 +818,16 @@ def _min(a, b)
fin
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Return the absolute value of a number
def abs(n)
if n < 0
return -n
else
return n
fin
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Read a string from the keyboard using the font manager, and intern it to the heap.
def _getStringResponse()
@ -1161,6 +1171,10 @@ def initMap(x, y, dir)
loadFrameImg()
mmgr(FINISH_LOAD, 1) // 1 = keep open
// Clear the list of encounter zones
global=>p_encounterZones = NULL
printf1("Cleared zones: $%x\n", global=>p_encounterZones)
// Start up the display engine with map data and starting position. This will also load and
// init the script module, if any, which will end up calling us back at the setScriptInfo
triggerTbl = NULL
@ -1173,23 +1187,53 @@ def initMap(x, y, dir)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Check for script(s) attached to the current location, and call them if there are any.
def checkScripts()
word x
word y
// Check for a random encounter at this position
def checkEncounter(x, y)
word p
word p_bestZone, bestDist
word d
// Find the zone that's closest, but not too far.
bestDist = INT_MAX
p_bestZone = NULL
p = global=>p_encounterZones
while p
d = min(abs(x - p=>w_encX), abs(y - p=>w_encY))
if d < bestDist and (p=>w_encMaxDist == 0 or d < p=>w_encMaxDist))
p_bestZone = p
bestDist = d
fin
p = p=>p_nextObj
loop
// Roll for an encounter in the zone. If none, get out early.
d = rand16() % 1000
if p_bestZone and d < p_bestZone=>w_encChance
// Encounter!
setWindow2()
clearWindow()
displayf1("Encounter! %s\n", p_bestZone=>s_name)
getYN()
clearWindow()
fin
end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Check for script(s) attached to the given location, and call them if there are any.
// Returns TRUE if any were triggered.
def checkScripts(x, y)
word p
word pNext
word script
byte anyTriggered
if !triggerTbl; return; fin
setWindow2()
getPos(@x, @y)
anyTriggered = FALSE
x = x - triggerOriginX
y = y - triggerOriginY
p = triggerTbl
while TRUE
while p
if ^p == $FF
return
break
fin
pNext = p + p->1
if ^p == y
@ -1197,13 +1241,17 @@ def checkScripts()
while p < pNext
if x == ^p
script = p=>1
setWindow2()
script()
anyTriggered = TRUE
fin
p = p + 3
loop
fin
p = pNext
loop
printf1("anyTriggered=%d\n", anyTriggered)
return anyTriggered
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -1232,6 +1280,7 @@ end
// Advance one step forward (3D maps only)
def moveForward()
byte val
word x, y
val = advance()
// If not blocked, render at the new position.
@ -1251,8 +1300,13 @@ def moveForward()
fin
// If there are script(s) on the new tile, run them.
getPos(@x, @y)
if val == 3
checkScripts()
if (!checkScripts(x, y))
checkEncounter(x, y)
fin
else
checkEncounter(x, y)
fin
end
@ -1792,7 +1846,7 @@ def restoreMapPos()
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def _makeEnemy(name, healthDice, image0, image1, attackType, attackText, attackRange, chanceToHit, dmg, groupSize)
def _makeEnemy(name, healthDice, image0, image1, attackType, attackText, attackRange, chanceToHit, dmg, groupSize, code)
word p; p = mmgr(HEAP_ALLOC, TYPE_ENEMY)
p=>s_name = mmgr(HEAP_INTERN, name)
p=>w_health = rollDice(healthDice) // 4d6
@ -1807,9 +1861,22 @@ def _makeEnemy(name, healthDice, image0, image1, attackType, attackText, attackR
p->b_chanceToHit = chanceToHit
p=>r_enemyDmg = dmg
p=>r_groupSize = groupSize
p=>s_enemyCode = mmgr(HEAP_INTERN, code)
return p
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def _addEncounterZone(code, x, y, dist, chance)
word p; p = mmgr(HEAP_ALLOC, TYPE_ENCOUNTER_ZONE)
p=>s_name = mmgr(HEAP_INTERN, code)
p=>w_encX = x
p=>w_encY = y
p=>w_encMaxDist = dist
p=>w_encChance = chance
addToList(@global=>p_encounterZones, p)
printf1("Added zone: $%x\n", global=>p_encounterZones)
end
///////////////////////////////////////////////////////////////////////////////////////////////////
def _strcmpi(a, b)
word limit, lenDiff, diff

View File

@ -8,7 +8,7 @@
// governing permissions and limitations under the License.
///////////////////////////////////////////////////////////////////////////////////////////////////
byte typeTbl_Global[] = Global, p_players, p_enemyGroups, p_combatFirst, 0
byte typeTbl_Global[] = Global, p_players, p_enemyGroups, p_combatFirst, p_encounterZones, 0
byte typeTbl_Player[] = Player, p_nextObj, s_name, p_combatNext, p_skills, p_items, p_buffs, p_debuffs, 0
byte typeTbl_Modifier[] = Modifier, p_nextObj, 0
byte typeTbl_Effect[] = Effect, p_nextObj, s_effectDescrip, 0
@ -18,9 +18,11 @@ byte typeTbl_Armor[] = Armor, p_nextObj, s_name, p_modifiers, 0
byte typeTbl_Stuff[] = Stuff, p_nextObj, s_name, 0
byte typeTbl_Enemy[] = Enemy, p_nextObj, s_name, p_combatNext, s_attackText, 0
byte typeTbl_EnemyGroup[] = EnemyGroup, p_nextObj, p_enemies, 0
byte typeTbl_EncounterZone[] = EncounterZone, p_nextObj, s_name, 0
word typeTbls = @typeTbl_Global, @typeTbl_Player, @typeTbl_Modifier, @typeTbl_Effect, @typeTbl_Item
word = @typeTbl_Weapon, @typeTbl_Armor, @typeTbl_Stuff, @typeTbl_Enemy, @typeTbl_EnemyGroup
word = @typeTbl_EncounterZone
word = 0
byte[] kind_bow_str = "bow(s)"

View File

@ -17,6 +17,7 @@ struc Global
word p_players
word p_enemyGroups
word p_combatFirst
word p_encounterZones
// Map position
byte b_mapIs3D
@ -170,6 +171,7 @@ struc Enemy
byte b_chanceToHit
word r_enemyDmg // 3 hex digits: num dice, die size, add. E.g. $361 = 3d6+1
word r_groupSize // number encountered, as 3 hex digits for dice
word s_enemyCode
end
const TYPE_ENEMY_GROUP = $89
@ -180,6 +182,17 @@ struc EnemyGroup
byte b_enemyGroupRange
end
const TYPE_ENCOUNTER_ZONE = $8A
struc EncounterZone
byte t_type
word p_nextObj
word s_name // enemy code
word w_encX
word w_encY
word w_encMaxDist
word w_encChance
end
// Weapon kinds
const KIND_BOW = 1;
const KIND_BLADE = 2;