UNPKG

avg-storyscript

Version:

Build-in Story Script System for AVG.js.

234 lines (220 loc) 7.17 kB
/*! * Copyright 2016 Icemic Jia <bingfeng.web@gmail.com> * * Licensed under the Apache License, Version 2.0 (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-2.0 * * 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. */ var parser = require('./libs/parser'); var variable = require('./libs/variable'); var { IfBlock, WhileBlock, ForeachBlock, InsertedBlock } = require('./libs/block'); export default class StoryScript { constructor(onGlobalChanged) { this.BLOCKSTACK = []; this.CURRENTBLOCK = null; this.onGlobalChanged = onGlobalChanged; } load(string) { const result = parser.parse(string); const system = new IfBlock(result); this.CURRENTBLOCK = system; this.BLOCKSTACK = []; // variable.reset(); } getBlockData() { const blocks = []; for (const [node, block] of [...this.BLOCKSTACK, this.CURRENTBLOCK].reverse().entries()) { let blockData = block.getData(); blockData.scope = variable.getScope(node); blocks.push(blockData); } return blocks.reverse(); } getGlobalScope() { return variable.getGlobalScope(); } getSaveScope() { return variable.getSaveScope(); } // @deprecated getData() { console.warn('[Storyscript] getData() has been deprecated!'); return { blocks: this.getBlockData(), globalScope: this.getGlobalScope(), saveScope: this.getSaveScope() } } setGlobalScope(scope) { variable.setGlobalScope(scope); } setSaveScope(scope) { variable.setSaveScope(scope); } setBlockData(blocks) { const scopes = [blocks[0].scope]; variable.setScopes(scopes); this.CURRENTBLOCK.setCurrentLine(blocks[0].currentLine); if (blocks.length === 1) { return true } for (let i = 0; i < blocks.length - 1; i++) { const block = blocks[i]; const nextBlock = blocks[i + 1]; const lastLine = block.currentLine - 1; const line = this.CURRENTBLOCK.getLine(lastLine); if (line.name === nextBlock.type) { switch (line.name) { case 'if': const ifBlock = new IfBlock(line.blocks[nextBlock.blockIndex], nextBlock.blockIndex); ifBlock.setCurrentLine(nextBlock.currentLine); variable.pushScope(nextBlock.scope); // variable.popScope(); this.BLOCKSTACK.push(this.CURRENTBLOCK); this.CURRENTBLOCK = ifBlock; break; case 'while': const whileBlock = new WhileBlock(line.block, line.condition); whileBlock.setCurrentLine(nextBlock.currentLine); variable.pushScope(nextBlock.scope); // variable.popScope(); this.BLOCKSTACK.push(this.CURRENTBLOCK); this.CURRENTBLOCK = whileBlock; break; case 'foreach': const foreachBlock = new ForeachBlock(line.block, line.child, line.children); foreachBlock.setCurrentLine(nextBlock.currentLine); variable.pushScope(nextBlock.scope); // variable.popScope(); this.BLOCKSTACK.push(this.CURRENTBLOCK); this.CURRENTBLOCK = foreachBlock; break; default: throw 'Bad savedata'; } } else if (nextBlock.type === 'inserted') { const insertedBlock = new InsertedBlock(nextBlock.data); insertedBlock.setCurrentLine(nextBlock.currentLine); variable.pushScope(nextBlock.scope); // variable.popScope(); this.BLOCKSTACK.push(this.CURRENTBLOCK); this.CURRENTBLOCK = insertedBlock; } else { throw 'Bad savedata'; } } } // @deprecated setData(object) { console.warn('[Storyscript] setData() has been deprecated!'); this.setGlobalScope(object.globalScope); this.setSaveScope(object.saveScope); this.setBlockData(object.blocks); } [Symbol.iterator]() { return this; } next() { let {value, done} = this.CURRENTBLOCK.next(); if (done) { var CURRENTBLOCK = this.BLOCKSTACK.pop(); if (CURRENTBLOCK) { this.CURRENTBLOCK = CURRENTBLOCK; variable.popScope(); return this.next(); } else { return { done: true } } } else { const retValue = this.handleScript(value); if (retValue) { return { value: retValue, done: false} } else { // handleLogic will return undefined, so should exec next line return this.next(); } } } handleScript(argLine) { // deep copy const line = Object.assign({}, argLine); if (line.type === 'content') { return this.handleContent(line); } else if (line.type === 'logic') { return this.handleLogic(line); } else if (line.type === 'comment') { return null; } else { throw `Unrecognized type ${line.type}`; } } handleContent(line) { const params = line.params; const keys = Object.keys(params); for (const key of keys) { params[key] = params[key].value; } return line; } handleLogic(line) { switch (line.name) { case 'if': return this.handleLogic_IF(line);break; case 'while': return this.handleLogic_WHILE(line);break; case 'foreach': return this.handleLogic_FOREACH(line);break; case 'let': return this.handleLogic_LET(line);break; default: throw `Unrecognized name ${line.name}`; } } handleLogic_IF(line) { let blockIndex = 0; for (const condition of line.conditions) { if (variable.calc(condition)) { break; } else { blockIndex++; } } this.BLOCKSTACK.push(this.CURRENTBLOCK); const blockData = line.blocks[blockIndex]; const block = new IfBlock(blockData, blockIndex); this.CURRENTBLOCK = block; // variable.pushScope(); } handleLogic_WHILE(line) { const result = variable.calc(line.condition); if (result) { this.BLOCKSTACK.push(this.CURRENTBLOCK); const blockData = line.block; const block = new WhileBlock(blockData, line.condition); this.CURRENTBLOCK = block; } // variable.pushScope(); } handleLogic_FOREACH(line) { const children = variable.calc(line.children); if (children instanceof Array) { this.BLOCKSTACK.push(this.CURRENTBLOCK); const blockData = line.block; const block = new ForeachBlock(blockData, line.child, line.children); this.CURRENTBLOCK = block; } else { throw '[Foreach] Children must be a array'; } // variable.pushScope(); } handleLogic_LET(line) { if (line.left.prefix === '$') { this.onGlobalChanged && this.onGlobalChanged(); } variable.assign(line.left.value, line.left.prefix, line.right, line.explicit); } } // module.exports = StoryScript;