Skip to content

Commit

Permalink
Merge pull request #1077 from y-lohse/upgrade/2.3.0
Browse files Browse the repository at this point in the history
Release 2.3.0
  • Loading branch information
smwhr authored Aug 28, 2024
2 parents d39664b + 755d48c commit 87db436
Show file tree
Hide file tree
Showing 16 changed files with 214 additions and 64 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,4 @@ import { Identifier } from 'inkjs/compiler/Parser/ParsedHierarchy/Identifier';
| 0.9.0 | 1.11.0 | 19 |
| 1.0.0 | 2.0.0 - 2.1.0 | 20 |
| 1.1.1 | 2.2.* | 21 |
| 1.2.0 | 2.3.0 | |
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": "2.2.5",
"version": "2.3.0",
"description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)",
"type": "commonjs",
"main": "dist/ink-full.js",
Expand Down
14 changes: 14 additions & 0 deletions src/compiler/Parser/InkParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,15 @@ export class InkParser extends StringParser {
new CharacterSet()
);

public static readonly Latin1Supplement: CharacterRange =
CharacterRange.Define("\u0080", "\u00FF", new CharacterSet());

public static readonly Chinese: CharacterRange = CharacterRange.Define(
"\u4E00",
"\u9FFF",
new CharacterSet()
);

private readonly ExtendIdentifierCharacterRanges = (
identifierCharSet: CharacterSet
): void => {
Expand Down Expand Up @@ -376,6 +385,8 @@ export class InkParser extends StringParser {
InkParser.Greek,
InkParser.Hebrew,
InkParser.Korean,
InkParser.Latin1Supplement,
InkParser.Chinese,
];

/**
Expand Down Expand Up @@ -415,6 +426,9 @@ export class InkParser extends StringParser {

this.Whitespace();

// Allow optional newline right after a choice name
if (optionalName != null) this.Newline();

// Optional condition for whether the choice should be shown to the player
const conditionExpr: Expression = this.Parse(
this.ChoiceCondition
Expand Down
24 changes: 15 additions & 9 deletions src/engine/CallStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export class CallStack {
name: string | null,
contextIndex: number = -1
) {
// contextIndex 0 means global, so index is actually 1-based
if (contextIndex == -1) contextIndex = this.currentElementIndex + 1;

let contextElement = this.callStack[contextIndex - 1];
Expand Down Expand Up @@ -359,16 +360,21 @@ export namespace CallStack {
". Has the story changed since this save data was created?"
);
else if (threadPointerResult.approximate) {
if (pointer.container === null) {
return throwNullException("pointer.container");
if (pointer.container !== null) {
storyContext.Warning(
"When loading state, exact internal story location couldn't be found: '" +
currentContainerPathStr +
"', so it was approximated to '" +
pointer.container.path.toString() +
"' to recover. Has the story changed since this save data was created?"
);
} else {
storyContext.Warning(
"When loading state, exact internal story location couldn't be found: '" +
currentContainerPathStr +
"' and it may not be recoverable. Has the story changed since this save data was created?"
);
}
storyContext.Warning(
"When loading state, exact internal story location couldn't be found: '" +
currentContainerPathStr +
"', so it was approximated to '" +
pointer.container.path.toString() +
"' to recover. Has the story changed since this save data was created?"
);
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/engine/Choice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,18 @@ export class Choice extends InkObject {
set pathStringOnChoice(value: string) {
this.targetPath = new Path(value);
}

public Clone() {
let copy = new Choice();
copy.text = this.text;
copy.sourcePath = this.sourcePath;
copy.index = this.index;
copy.targetPath = this.targetPath;
copy.originalThreadIndex = this.originalThreadIndex;
copy.isInvisibleDefault = this.isInvisibleDefault;
if (this.threadAtGeneration !== null)
copy.threadAtGeneration = this.threadAtGeneration.Copy();

return copy;
}
}
13 changes: 12 additions & 1 deletion src/engine/Container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,24 @@ export class Container extends InkObject implements INamedContent {
let foundObj: InkObject | null =
currentContainer.ContentWithPathComponent(comp);

// Couldn't resolve entire path?
if (foundObj == null) {
result.approximate = true;
break;
}

// Are we about to loop into another container?
// Is the object a container as expected? It might
// no longer be if the content has shuffled around, so what
// was originally a container no longer is.
const nextContainer: Container | null = asOrNull(foundObj, Container);
if (i < partialPathLength - 1 && nextContainer == null) {
result.approximate = true;
break;
}

currentObj = foundObj;
currentContainer = asOrNull(foundObj, Container);
currentContainer = nextContainer;
}

result.obj = currentObj;
Expand Down
46 changes: 33 additions & 13 deletions src/engine/InkList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export class InkList extends Map<SerializedInkListItem, number> {
}

public static FromString(myListItem: string, originStory: Story) {
if (myListItem == null || myListItem == "") return new InkList();
let listValue =
originStory.listDefinitions?.FindSingleItemListWithName(myListItem);
if (listValue) {
Expand All @@ -186,7 +187,10 @@ export class InkList extends Map<SerializedInkListItem, number> {
}
}

public AddItem(itemOrItemName: InkListItem | string | null) {
public AddItem(
itemOrItemName: InkListItem | string | null,
storyObject: Story | null = null
) {
if (itemOrItemName instanceof InkListItem) {
let item = itemOrItemName;

Expand Down Expand Up @@ -216,8 +220,9 @@ export class InkList extends Map<SerializedInkListItem, number> {
throw new Error(
"Failed to add item to list because the item was from a new list definition that wasn't previously known to this list. Only items from previously known lists can be used, so that the int value can be found."
);
} else {
let itemName = itemOrItemName as string | null;
} else if (itemOrItemName !== null) {
//itemOrItemName is a string
let itemName = itemOrItemName as string;

let foundListDef: ListDefinition | null = null;

Expand All @@ -242,16 +247,23 @@ export class InkList extends Map<SerializedInkListItem, number> {
}
}

if (foundListDef == null)
throw new Error(
"Could not add the item " +
itemName +
" to this list because it isn't known to any list definitions previously associated with this list."
);

let item = new InkListItem(foundListDef.name, itemName);
let itemVal = foundListDef.ValueForItem(item);
this.Add(item, itemVal);
if (foundListDef == null) {
if (storyObject == null) {
throw new Error(
"Could not add the item " +
itemName +
" to this list because it isn't known to any list definitions previously associated with this list."
);
} else {
let newItem = InkList.FromString(itemName, storyObject)
.orderedItems[0];
this.Add(newItem.Key, newItem.Value);
}
} else {
let item = new InkListItem(foundListDef.name, itemName);
let itemVal = foundListDef.ValueForItem(item);
this.Add(item, itemVal);
}
}
}
public ContainsItemNamed(itemName: string | null) {
Expand Down Expand Up @@ -519,6 +531,14 @@ export class InkList extends Map<SerializedInkListItem, number> {

return ordered;
}

get singleItem(): InkListItem | null {
for (let item of this.orderedItems) {
return item.Key;
}
return null;
}

public toString() {
let ordered = this.orderedItems;

Expand Down
34 changes: 21 additions & 13 deletions src/engine/JsonSerialisation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,16 @@ export class JsonSerialisation {
choice.sourcePath = jObj["originalChoicePath"].toString();
choice.originalThreadIndex = parseInt(jObj["originalThreadIndex"]);
choice.pathStringOnChoice = jObj["targetPath"].toString();
choice.tags = this.JArrayToTags(jObj);
return choice;
}

public static JArrayToTags(jObj: Record<string, any>) {
if (jObj["tags"]) {
choice.tags = jObj["tags"];
return jObj["tags"];
} else {
return null;
}
return choice;
}

public static WriteChoice(writer: SimpleJson.Writer, choice: Choice) {
Expand All @@ -593,20 +599,22 @@ export class JsonSerialisation {
writer.WriteProperty("originalChoicePath", choice.sourcePath);
writer.WriteIntProperty("originalThreadIndex", choice.originalThreadIndex);
writer.WriteProperty("targetPath", choice.pathStringOnChoice);
if (choice.tags) {
writer.WriteProperty("tags", (w) => {
w.WriteArrayStart();
for (const tag of choice.tags!) {
w.WriteStringStart();
w.WriteStringInner(tag);
w.WriteStringEnd();
}
w.WriteArrayEnd();
});
}
this.WriteChoiceTags(writer, choice);
writer.WriteObjectEnd();
}

public static WriteChoiceTags(writer: SimpleJson.Writer, choice: Choice) {
if (choice.tags && choice.tags.length > 0) {
writer.WritePropertyStart("tags");
writer.WriteArrayStart();
for (const tag of choice.tags!) {
writer.Write(tag);
}
writer.WriteArrayEnd();
writer.WritePropertyEnd();
}
}

public static WriteInkList(writer: SimpleJson.Writer, listVal: ListValue) {
let rawList = listVal.value;
if (rawList === null) {
Expand Down
4 changes: 3 additions & 1 deletion src/engine/NativeFunctionCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ export class NativeFunctionCall extends InkObject {
for (let p of parameters) {
if (p instanceof Void)
throw new StoryException(
'Attempting to perform operation on a void value. Did you forget to "return" a value from a function you called here?'
"Attempting to perform " +
this.name +
' on a void value. Did you forget to "return" a value from a function you called here?'
);
if (p instanceof ListValue) hasList = true;
}
Expand Down
33 changes: 29 additions & 4 deletions src/engine/Story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@ export class Story extends InkObject {
this._state.ResetOutput();

if (this._recursiveContinueCount == 1)
this._state.variablesState.batchObservingVariableChanges = true;
this._state.variablesState.StartVariableObservation();
} else if (this._asyncContinueActive && !isAsyncTimeLimited) {
this._asyncContinueActive = false;
}

let durationStopwatch = new Stopwatch();
Expand Down Expand Up @@ -400,6 +402,8 @@ export class Story extends InkObject {

durationStopwatch.Stop();

let changedVariablesToObserve: Map<string, any> | null = null;

if (outputStreamEndsInNewline || !this.canContinue) {
if (this._stateSnapshotAtLastNewline !== null) {
this.RestoreStateSnapshot();
Expand Down Expand Up @@ -439,7 +443,8 @@ export class Story extends InkObject {
this._sawLookaheadUnsafeFunctionAfterNewline = false;

if (this._recursiveContinueCount == 1)
this._state.variablesState.batchObservingVariableChanges = false;
changedVariablesToObserve =
this._state.variablesState.CompleteVariableObservation();

this._asyncContinueActive = false;
if (this.onDidContinue !== null) this.onDidContinue();
Expand Down Expand Up @@ -494,6 +499,12 @@ export class Story extends InkObject {
throw new StoryException(sb.toString());
}
}
if (
changedVariablesToObserve != null &&
Object.keys(changedVariablesToObserve).length > 0
) {
this._state.variablesState.NotifyObservers(changedVariablesToObserve);
}
}

public ContinueSingleStep() {
Expand Down Expand Up @@ -664,7 +675,7 @@ export class Story extends InkObject {

public StateSnapshot() {
this._stateSnapshotAtLastNewline = this._state;
this._state = this._state.CopyAndStartPatching();
this._state = this._state.CopyAndStartPatching(false);
}

public RestoreStateSnapshot() {
Expand Down Expand Up @@ -696,7 +707,7 @@ export class Story extends InkObject {
);

let stateToSave = this._state;
this._state = this._state.CopyAndStartPatching();
this._state = this._state.CopyAndStartPatching(true);
this._asyncSaving = true;
return stateToSave;
}
Expand Down Expand Up @@ -1836,6 +1847,20 @@ export class Story extends InkObject {

let foundExternal = typeof funcDef !== "undefined";

if (
foundExternal &&
!funcDef!.lookAheadSafe &&
this._state.inStringEvaluation
) {
this.Error(
"External function " +
funcName +
' could not be called because 1) it wasn\'t marked as lookaheadSafe when BindExternalFunction was called and 2) the story is in the middle of string generation, either because choice text is being generated, or because you have ink like "hello {func()}". You can work around this by generating the result of your function into a temporary variable before the string or choice gets generated: ~ temp x = ' +
funcName +
"()"
);
}

if (
foundExternal &&
!funcDef!.lookAheadSafe &&
Expand Down
Loading

0 comments on commit 87db436

Please sign in to comment.