Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
y-lohse committed Oct 12, 2016
2 parents 511d280 + 251c792 commit 07c884e
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 80 deletions.
3 changes: 3 additions & 0 deletions engine/Choice.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ export class Choice{
get pathStringOnChoice(){
return this.choicePoint.pathStringOnChoice;
}
get sourcePath(){
return this.choicePoint.path.componentsString;
}
}
18 changes: 15 additions & 3 deletions engine/ChoicePoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Path} from './Path';
export class ChoicePoint extends InkObject{
constructor(onceOnly){
super();
this.pathOnChoice;
this._pathOnChoice;
this.hasCondition;
this.hasStartContent;
this.hasChoiceOnlyContent;
Expand All @@ -13,9 +13,18 @@ export class ChoicePoint extends InkObject{

this.onceOnly = !!onceOnly;
}
get pathOnChoice(){
if (this._pathOnChoice != null && this._pathOnChoice.isRelative) {
var choiceTargetObj = this.choiceTarget;
if (choiceTargetObj) {
this._pathOnChoice = choiceTargetObj.path;
}
}
return this._pathOnChoice;
}
get choiceTarget(){
//return this.ResolvePath (pathOnChoice) as Container;
return this.ResolvePath(this.pathOnChoice);
//return this.ResolvePath (_pathOnChoice) as Container;
return this.ResolvePath(this._pathOnChoice);
}
get pathStringOnChoice(){
return this.CompactPathString(this.pathOnChoice);
Expand All @@ -39,6 +48,9 @@ export class ChoicePoint extends InkObject{
this.isInvisibleDefault = (value & 8) > 0;
this.onceOnly = (value & 16) > 0;
}
set pathOnChoice(value){
this._pathOnChoice = value;
}

toString(){
// int? targetLineNum = DebugLineNumberOfPath (pathOnChoice);
Expand Down
106 changes: 32 additions & 74 deletions engine/Story.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import {Container} from './Container';
import {Object as InkObject} from './Object';
import {JsonSerialisation} from './JsonSerialisation';
import {StoryState} from './StoryState';
import {CallStack} from './CallStack';
import {ControlCommand} from './ControlCommand';
import {PushPopType} from './PushPop';
import {ChoicePoint} from './ChoicePoint';
import {Choice} from './Choice';
import {Divert} from './Divert';
import {ValueType, Value, StringValue, IntValue, DivertTargetValue, VariablePointerValue} from './Value';
import {Value, StringValue, IntValue, DivertTargetValue, VariablePointerValue} from './Value';
import {Path} from './Path';
import {Void} from './Void';
import {Tag} from './Tag';
Expand All @@ -24,8 +23,8 @@ export class Story extends InkObject{
constructor(jsonString){
super();

this.inkVersionCurrent = 14;
this.inkVersionMinimumCompatible = 12;
this.inkVersionCurrent = 15;
this.inkVersionMinimumCompatible = 15;

this._variableObservers = null;
this._externals = {};
Expand Down Expand Up @@ -628,17 +627,31 @@ export class Story extends InkObject{

var popType = evalCommand.commandType == ControlCommand.CommandType.PopFunction ?
PushPopType.Function : PushPopType.Tunnel;

var overrideTunnelReturnTarget = null;
if (popType == PushPopType.Tunnel) {
var popped = this.state.PopEvaluationStack();
// overrideTunnelReturnTarget = popped as DivertTargetValue;
overrideTunnelReturnTarget = popped;
if (overrideTunnelReturnTarget instanceof DivertTargetValue === false) {
if (popped instanceof Void === false){
throw "Expected void if ->-> doesn't override target";
}
}
}

if (this.state.callStack.currentElement.type != popType || !this.state.callStack.canPop) {
if (this.state.TryExitExternalFunctionEvaluation()){
break;
}
else if (this.state.callStack.currentElement.type != popType || !this.state.callStack.canPop) {

var names = new {};
var names = {};
names[PushPopType.Function] = "function return statement (~ return)";
names[PushPopType.Tunnel] = "tunnel onwards statement (->->)";

var expected = names[this.state.callStack.currentElement.type];
if (!this.state.callStack.canPop) {
if (!this.state.callStack.canPop)
expected = "end of flow (-> END or choice)";
}

var errorMsg = "Found " + names[popType] + ", when expected " + expected;

Expand All @@ -647,6 +660,9 @@ export class Story extends InkObject{

else {
this.state.callStack.Pop();

if (overrideTunnelReturnTarget)
this.state.divertedTargetObject = this.ContentAtPath(overrideTunnelReturnTarget.targetPath);
}
break;

Expand Down Expand Up @@ -909,80 +925,19 @@ export class Story extends InkObject{
else
throw e;
}

// We'll start a new callstack, so keep hold of the original,
// as well as the evaluation stack so we know if the function
// returned something
var originalCallstack = this.state.callStack;
var originalEvaluationStackHeight = this.state.evaluationStack.length;

// Create a new base call stack element.
// By making it point at element 0 of the base, when NextContent is
// called, it'll actually step past the entire content of the game (!)
// and straight onto the Done. Bit of a hack :-/ We don't really have
// a better way of creating a temporary context that ends correctly.
this.state.callStack = new CallStack(this.mainContentContainer);
this.state.callStack.currentElement.currentContainer = this.mainContentContainer;
this.state.callStack.currentElement.currentContentIndex = 0;

if (args != null) {
for (var i = 0; i < args.length; i++) {
if (!(typeof args[i] === 'number' || typeof args[i] === 'string')) {
throw "ink arguments when calling EvaluateFunction must be int, float or string";
}

this.state.evaluationStack.push(Value.Create(args[i]));
}
}

// Jump into the function!
this.state.callStack.Push(PushPopType.Function);
this.state.currentContentObject = funcContainer;


this.state.StartExternalFunctionEvaluation(funcContainer, args);

// Evaluate the function, and collect the string output
var stringOutput = new StringBuilder();
while (this.canContinue) {
stringOutput.Append(this.Continue());
}
var textOutput = stringOutput.toString();

// Restore original stack
this.state.callStack = originalCallstack;

// Do we have a returned value?
// Potentially pop multiple values off the stack, in case we need
// to clean up after ourselves (e.g. caller of EvaluateFunction may
// have passed too many arguments, and we currently have no way to check for that)
var returnedObj = null;
while (this.state.evaluationStack.length > originalEvaluationStackHeight) {
var poppedObj = this.state.PopEvaluationStack();
if (returnedObj == null)
returnedObj = poppedObj;
}

//inkjs specific: since we change the type of return conditionally, we want to have only one return statement
var returnedValue = null;

if (returnedObj) {
if (returnedObj instanceof Void)
returnedValue = null;
var result = this.state.CompleteExternalFunctionEvaluation();

// Some kind of value, if not void
// var returnVal = returnedObj as Runtime.Value;
var returnVal = returnedObj;

// DivertTargets get returned as the string of components
// (rather than a Path, which isn't public)
if (returnVal.valueType == ValueType.DivertTarget) {
returnedValue = returnVal.valueObject.toString();
}

// Other types can just have their exact object type:
// int, float, string. VariablePointers get returned as strings.
returnedValue = returnVal.valueObject;
}

return (returnTextOutput) ? {'returned': returnedValue, 'output': textOutput} : returnedValue;
return (returnTextOutput) ? {'returned': result, 'output': textOutput} : result;
}
EvaluateExpression(exprContainer){
var startCallStackHeight = this.state.callStack.elements.length;
Expand Down Expand Up @@ -1277,6 +1232,9 @@ export class Story extends InkObject{

didPop = true;
}
else {
this.state.TryExitExternalFunctionEvaluation();
}

// Step past the point where we last called out
if (didPop && this.state.currentContentObject != null) {
Expand Down
82 changes: 81 additions & 1 deletion engine/StoryState.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {CallStack} from './CallStack';
import {VariablesState} from './VariablesState';
import {StringValue} from './Value';
import {ValueType, Value, StringValue} from './Value';
import {PushPopType} from './PushPop';
import {Tag} from './Tag';
import {Glue} from './Glue';
import {Path} from './Path';
Expand All @@ -10,6 +11,7 @@ import {StringBuilder} from './StringBuilder';
import {JsonSerialisation} from './JsonSerialisation';
import {Story} from './Story';
import {PRNG} from './PRNG';
import {Void} from './Void';

export class StoryState{
constructor(story){
Expand Down Expand Up @@ -37,6 +39,10 @@ export class StoryState{
this._currentErrors = null;

this.didSafeExit = false;

this._isExternalFunctionEvaluation = false;
this._originalCallstack = null;
this._originalEvaluationStackHeight = 0;

this.GoToStart();
}
Expand Down Expand Up @@ -534,6 +540,80 @@ export class StoryState{

this._currentTurnIndex++;
}
StartExternalFunctionEvaluation(funcContainer, args){
// We'll start a new callstack, so keep hold of the original,
// as well as the evaluation stack so we know if the function
// returned something
this._originalCallstack = this.callStack;
this._originalEvaluationStackHeight = this.evaluationStack.length;

// Create a new base call stack element.
this.callStack = new CallStack(funcContainer);
this.callStack.currentElement.type = PushPopType.Function;

// By setting ourselves in external function evaluation mode,
// we're saying it's okay to end the flow without a Done or End,
// but with a ~ return instead.
this._isExternalFunctionEvaluation = true;

// Pass arguments onto the evaluation stack
if (args != null) {
for (var i = 0; i < args.length; i++) {
if (!(typeof args[i] === 'number' || typeof args[i] === 'string')) {
throw "ink arguments when calling EvaluateFunction must be int, float or string";
}

this.evaluationStack.push(Value.Create(args[i]));
}
}
}
TryExitExternalFunctionEvaluation(){
if (this._isExternalFunctionEvaluation && this.callStack.elements.length == 1 && this.callStack.currentElement.type == PushPopType.Function) {
this.currentContentObject = null;
this.didSafeExit = true;
return true;
}

return false;
}
CompleteExternalFunctionEvaluation(){
// Do we have a returned value?
// Potentially pop multiple values off the stack, in case we need
// to clean up after ourselves (e.g. caller of EvaluateFunction may
// have passed too many arguments, and we currently have no way to check for that)
var returnedObj = null;
while (this.evaluationStack.length > this._originalEvaluationStackHeight) {
var poppedObj = this.PopEvaluationStack();
if (returnedObj == null)
returnedObj = poppedObj;
}

// Restore our own state
this.callStack = this._originalCallstack;
this._originalCallstack = null;
this._originalEvaluationStackHeight = 0;

if (returnedObj) {
if (returnedObj instanceof Void)
return null;

// Some kind of value, if not void
// var returnVal = returnedObj as Runtime.Value;
var returnVal = returnedObj;

// DivertTargets get returned as the string of components
// (rather than a Path, which isn't public)
if (returnVal.valueType == ValueType.DivertTarget) {
return returnVal.valueObject.toString();
}

// Other types can just have their exact object type:
// int, float, string. VariablePointers get returned as strings.
return returnVal.valueObject;
}

return null;
}
AddError(message){
if (this._currentErrors == null) {
this._currentErrors = [];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "inkjs",
"version": "1.3.1",
"version": "1.4.0",
"description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)",
"main": "dist/ink-es2015.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion test/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var inkFile = fs.readFileSync(__dirname + '/stories/test.ink.json', 'UTF-8').rep
var s = new Story(inkFile);

//console.log(s.BuildStringOfHierarchy());
console.log(s.EvaluateFunction('describe_health', [50], true));
//console.log(s.EvaluateFunction('describe_health', [50], true));

//end();
var gameSave;
Expand Down

0 comments on commit 07c884e

Please sign in to comment.