UNPKG

@bitsy/hecks

Version:

a collection of re-usable scripts for bitsy game maker

295 lines (276 loc) • 7.72 kB
/** šŸ”€ @file dialog choices @summary binary dialog choices @license MIT @version 3.0.0 @requires 5.3 @author Sean S. LeBlanc @description Adds a dialog tag which allows you to present the player with binary dialog choices. Uses as an arrow cursor by default, but this can be changed in the hackOptions to use a custom bitsy sprite instead. Usage: {choice - option one result of picking option - option two result of picking option } Recommended uses: DLG_simple_response """ Greeting text {choice - Response one answer to response one - Response two answer to response two } """ DLG_complex_response """ Greeting text {choice - Response one {a = 1} - Response two {a = 2} } constant part of answer{ - a == 1 ? custom part based on response one - a == 2 ? custom part based on response two } """ Note: it's recommended you combine this hack with the dialog jump hack for complex cases. Limitations: Each option must fit on a single line, or the interaction will break. Checking the value of a variable set in an option *immediately after the choice* will not work, as it will evaluate before the player has selected an option if there is no text inbetween the two. e.g. """ {a = 1} {choice - Response one {a = 2} - Response two {a = 3} } { - a == 1 ? this will print - a == 2 ? these will not - a == 3 ? these will not } """ HOW TO USE: 1. Copy-paste into a script tag after the bitsy source 2. Edit hackOptions below as needed */ import bitsy from "bitsy"; import { inject, } from "./helpers/kitsy-script-toolkit"; import "./helpers/addParagraphBreak"; var hackOptions = { // if defined, the cursor is drawn as the sprite with the given id // e.g. replace with `getCursorSprite('A')` to use the player's avatar as a cursor // if not defined, uses an arrow graphic similar to the continue arrow cursor: getCursorSprite(), // modifies the scale/position of the cursor // recommended combinations: // - scale: 4, y: 1, x: 0 // - scale: 2, y: 3, x: 1 // - scale: 2, y: 4, x: 0 + custom cursor transform: { scale: bitsy.scale, y: 1, x: 0, } }; var dialogChoices = { choice: 0, choices: [], choicesActive: false, swiped: false, handleInput: function (dialogBuffer) { if (!this.choicesActive) { this.swiped = false; return false; } var pswiped = this.swiped; var swiped = !pswiped && (bitsy.input.swipeUp() || bitsy.input.swipeDown() || bitsy.input.swipeRight()); if (swiped) { this.swiped = true; } else if (!bitsy.input.swipeUp() && !bitsy.input.swipeDown() && !bitsy.input.swipeRight()) { this.swiped = false; } var l = Math.max(this.choices.length, 1); // navigate if ( (bitsy.input.anyKeyPressed() && (bitsy.input.isKeyDown(bitsy.key.up) || bitsy.input.isKeyDown(bitsy.key.w))) || (swiped && bitsy.input.swipeUp()) ) { this.choice -= 1; if (this.choice < 0) { this.choice += l; } return false; } if ( (bitsy.input.anyKeyPressed() && (bitsy.input.isKeyDown(bitsy.key.down) || bitsy.input.isKeyDown(bitsy.key.s))) || (swiped && bitsy.input.swipeDown()) ) { this.choice = (this.choice + 1) % l; return false; } // select if ( ((bitsy.input.anyKeyPressed() && ( bitsy.input.isKeyDown(bitsy.key.right) || bitsy.input.isKeyDown(bitsy.key.d) || bitsy.input.isKeyDown(bitsy.key.enter) || bitsy.input.isKeyDown(bitsy.key.space) ) ) || (swiped && bitsy.input.swipeRight())) ) { // evaluate choice this.choices[this.choice](); // reset this.choice = 0; this.choices = []; this.choicesActive = false; // get back into the regular dialog flow if (dialogBuffer.Continue()) { dialogBuffer.Update(0); // make sure to close dialog if there's nothing to say // after the choice has been made if (!dialogBuffer.CurCharCount()) { dialogBuffer.Continue(); } } return true; } return false; } }; bitsy.dialogChoices = dialogChoices; function getCursorSprite(cursor) { return cursor ? `renderer.GetImageSource(sprite['${cursor}'].drw)[sprite['${cursor}'].animation.frameIndex]` : `[ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]`; } // parsing // (adds a new sequence node type) inject(/(\|\| str === "shuffle")/, '$1 || str === "choice"'); inject(/(state\.curNode\.AddChild\( new ShuffleNode\( options \) \);)/, `$1 else if(sequenceType === "choice") state.curNode.AddChild( new ChoiceNode( options ) ); `); inject(/(var ShuffleNode = )/, ` var ChoiceNode = function(options) { Object.assign( this, new TreeRelationship() ); Object.assign( this, new SequenceBase() ); this.type = "choice"; this.options = options; options.forEach(function(option){ var br = option.children.find(function(child){ return child.name === 'br'; }); if (!br) { option.onSelect = []; return; } var idx = option.children.indexOf(br); option.onSelect = option.children.slice(idx+1); option.children = option.children.slice(0, idx); }); this.Eval = function(environment,onReturn) { var lastVal = null; var i = 0; function evalChildren(children,done) { if(i < children.length) { children[i].Eval(environment, function(val) { environment.GetDialogBuffer().AddLinebreak(); lastVal = val; i++; evalChildren(children,done); }); } else { done(); window.dialogChoices.choicesActive = true; } } window.dialogChoices.choices = this.options.map(function(option){ return function(){ option.onSelect.forEach(function(child){ child.Eval(environment, function(){}); }); }; }); if (environment.GetDialogBuffer().CurCharCount() > 0) { environment.GetDialogBuffer().AddParagraphBreak(); } evalChildren(this.options, function() { environment.GetDialogBuffer().AddParagraphBreak(); onReturn(lastVal); }); } } $1`); // rendering // (re-uses existing arrow image data, // but draws rotated to point at text) inject(/(this\.DrawNextArrow = )/, ` this.DrawChoiceArrow = function() { var rows = ${hackOptions.cursor}; var top = (${hackOptions.transform.y} + window.dialogChoices.choice * (textboxInfo.padding_vert + relativeFontHeight())) * scale; var left = ${hackOptions.transform.x}*scale; for (var y = 0; y < rows.length; y++) { var cols = rows[y]; for (var x = 0; x < cols.length; x++) { if (cols[x]) { //scaling nonsense for (var sy = 0; sy < ${hackOptions.transform.scale}; sy++) { for (var sx = 0; sx < ${hackOptions.transform.scale}; sx++) { var pxl = 4 * ( ((top+(y*${hackOptions.transform.scale})+sy) * (textboxInfo.width*scale)) + (left+(x*${hackOptions.transform.scale})+sx) ); textboxInfo.img.data[pxl+0] = 255; textboxInfo.img.data[pxl+1] = 255; textboxInfo.img.data[pxl+2] = 255; textboxInfo.img.data[pxl+3] = 255; } } } } } }; $1`); inject(/(this\.DrawTextbox\(\);)/, ` if (window.dialogChoices.choicesActive) { this.DrawChoiceArrow(); } $1`); // interaction // (overrides the dialog skip/page flip) inject(/(if\( dialogBuffer\.IsActive\(\) \) {)/, `$1 if(window.dialogChoices.handleInput(dialogBuffer)) { return; } else `); inject(/(this\.CanContinue = function\(\) {)/, `$1 if(window.dialogChoices.choicesActive){ return false; } `);