diff --git a/OutlawEditor/src/main/resources/mythos/mythos-editor/html/editor.html b/OutlawEditor/src/main/resources/mythos/mythos-editor/html/editor.html index 7cae2d72..89889ef8 100644 --- a/OutlawEditor/src/main/resources/mythos/mythos-editor/html/editor.html +++ b/OutlawEditor/src/main/resources/mythos/mythos-editor/html/editor.html @@ -67,6 +67,7 @@ + diff --git a/OutlawEditor/src/main/resources/mythos/mythos-editor/js/mythos_uncompressed.js b/OutlawEditor/src/main/resources/mythos/mythos-editor/js/mythos_uncompressed.js index 11f91857..998a90d4 100644 --- a/OutlawEditor/src/main/resources/mythos/mythos-editor/js/mythos_uncompressed.js +++ b/OutlawEditor/src/main/resources/mythos/mythos-editor/js/mythos_uncompressed.js @@ -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); diff --git a/Platform/Apple/tools/PackPartitions/src/org/demo/PackPartitions.groovy b/Platform/Apple/tools/PackPartitions/src/org/demo/PackPartitions.groovy index 729d281b..25011e31 100644 --- a/Platform/Apple/tools/PackPartitions/src/org/demo/PackPartitions.groovy +++ b/Platform/Apple/tools/PackPartitions/src/org/demo/PackPartitions.groovy @@ -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" } diff --git a/Platform/Apple/virtual/src/plasma/gamelib.plh b/Platform/Apple/virtual/src/plasma/gamelib.plh index 5d0ed401..92742671 100644 --- a/Platform/Apple/virtual/src/plasma/gamelib.plh +++ b/Platform/Apple/virtual/src/plasma/gamelib.plh @@ -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 diff --git a/Platform/Apple/virtual/src/plasma/gameloop.pla b/Platform/Apple/virtual/src/plasma/gameloop.pla index cf736f0a..27111bdc 100644 --- a/Platform/Apple/virtual/src/plasma/gameloop.pla +++ b/Platform/Apple/virtual/src/plasma/gameloop.pla @@ -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 diff --git a/Platform/Apple/virtual/src/plasma/playtype.pla b/Platform/Apple/virtual/src/plasma/playtype.pla index 424e0d47..688fd471 100644 --- a/Platform/Apple/virtual/src/plasma/playtype.pla +++ b/Platform/Apple/virtual/src/plasma/playtype.pla @@ -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)" diff --git a/Platform/Apple/virtual/src/plasma/playtype.plh b/Platform/Apple/virtual/src/plasma/playtype.plh index 0e2aa303..86f09f54 100644 --- a/Platform/Apple/virtual/src/plasma/playtype.plh +++ b/Platform/Apple/virtual/src/plasma/playtype.plh @@ -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;