├── docs ├── .nojekyll ├── logo │ ├── Logo.psd │ ├── icon-black.png │ ├── samwise-black.png │ ├── samwise-white.png │ ├── samwise-both-large.png │ └── icon.svg ├── images │ └── screenshot.jpg ├── Examples.md ├── Vscode_Advanced.md ├── Faq.md ├── _coverpage.md ├── Games.md ├── Acknowledgements.md ├── index.html ├── _sidebar.md ├── Language_CodeExtensions.md ├── Language_Metadata.md ├── Changelog.md ├── Vscode_Write.md ├── Home.md ├── Language_Challenge.md ├── Plugin_Unity.md ├── Quick_Start.md └── Language_Selection.md ├── samples ├── .vscode │ └── settings.json ├── multiFileTest2.sam ├── multiFileTest1.sam ├── .samwise │ └── avatars │ │ └── captain.png ├── test.sam └── dinner.sam ├── vscode ├── build_package.bat ├── images │ ├── icon.png │ ├── ss1.png │ ├── ss2.png │ ├── ss3.png │ ├── ss4.png │ ├── ss5.png │ ├── icon-data.png │ ├── icon-package.png │ ├── icon-player.png │ ├── icon-toolbox.png │ ├── icon-transp.png │ └── icon-light-transp.png ├── webview-ui │ ├── avatars │ │ ├── char1.png │ │ ├── char2.png │ │ ├── char3.png │ │ ├── char4.png │ │ ├── char5.png │ │ ├── char6.png │ │ ├── char7.png │ │ ├── char8.png │ │ ├── char9.png │ │ ├── char10.png │ │ ├── char11.png │ │ ├── char12.png │ │ ├── char13.png │ │ ├── char14.png │ │ ├── char15.png │ │ └── char16.png │ ├── BadComic-Italic.ttf │ ├── gutterDialogue.png │ ├── BadComic-Regular.ttf │ └── tool.js ├── .vscodeignore ├── .vscode │ ├── extensions.json │ ├── tasks.json │ ├── settings.json │ └── launch.json ├── tsconfig.json ├── .eslintrc.json ├── CHANGELOG.md ├── language-configuration.json ├── src │ ├── samwiseHover.ts │ ├── samwiseSymbols.ts │ ├── samwiseDefinitions.ts │ ├── common.ts │ ├── samwiseToolView.ts │ └── samwiseCodelens.ts └── README.md ├── .github └── FUNDING.yml ├── src ├── samwise.code-workspace ├── Samwise │ ├── Runtime │ │ ├── Code │ │ │ ├── IAsyncCode.cs │ │ │ ├── IStatement.cs │ │ │ ├── IValue.cs │ │ │ ├── IIntegerValue.cs │ │ │ ├── ISymbolValue.cs │ │ │ ├── VariableType.cs │ │ │ ├── IBoolValue.cs │ │ │ ├── IUnaryOperationValue.cs │ │ │ ├── IBinaryOperationValue.cs │ │ │ ├── NegateValue.cs │ │ │ ├── NotValue.cs │ │ │ ├── IncrementAssignmentStatement.cs │ │ │ ├── IntegerValue.cs │ │ │ ├── AndValue.cs │ │ │ ├── OrValue.cs │ │ │ ├── BoolValue.cs │ │ │ ├── AssignmentStatement.cs │ │ │ ├── OnceValue.cs │ │ │ └── SymbolValue.cs │ │ ├── Nodes │ │ │ ├── ICheckableContent.cs │ │ │ ├── ITimeable.cs │ │ │ ├── ICase.cs │ │ │ ├── IConditional.cs │ │ │ ├── IResolvableNode.cs │ │ │ ├── IAdvanceableNode.cs │ │ │ ├── ICheckable.cs │ │ │ ├── IAutoNode.cs │ │ │ ├── ISource.cs │ │ │ ├── IMultiCaseNode.cs │ │ │ ├── IReferencingNode.cs │ │ │ ├── ITextContent.cs │ │ │ ├── IContent.cs │ │ │ ├── ExitNode.cs │ │ │ ├── IBlockContainerNode.cs │ │ │ ├── NextNodeBehaviour.cs │ │ │ ├── AnonymousSequenceBlock.cs │ │ │ ├── IOption.cs │ │ │ ├── IStatefulElement.cs │ │ │ ├── IChoosableNode.cs │ │ │ ├── JoinNode.cs │ │ │ ├── StatefulSelectionNode.cs │ │ │ ├── CancelNode.cs │ │ │ ├── CaptionNode.cs │ │ │ ├── FallbackNode.cs │ │ │ ├── ITaggable.cs │ │ │ ├── SpeechNode.cs │ │ │ ├── CheckResultBlock.cs │ │ │ ├── WaitNode.cs │ │ │ ├── SingleBlock.cs │ │ │ ├── CodeNode.cs │ │ │ ├── TagData.cs │ │ │ ├── LoopNode.cs │ │ │ ├── IDialogueNode.cs │ │ │ ├── ClampNode.cs │ │ │ ├── SequenceBlock.cs │ │ │ ├── SelectionNode.cs │ │ │ ├── SelectionCase.cs │ │ │ ├── OptionGroup.cs │ │ │ ├── GotoNode.cs │ │ │ ├── AwaitNode.cs │ │ │ ├── CheckNode.cs │ │ │ ├── ScoreCase.cs │ │ │ ├── IDialogueBlock.cs │ │ │ ├── InterruptibleNode.cs │ │ │ ├── ChoiceNode.cs │ │ │ └── ScoreNode.cs │ │ ├── Machine │ │ │ ├── IExternalCodeMachine.cs │ │ │ ├── DialogueStatus.cs │ │ │ ├── IExternalCodeContext.cs │ │ │ └── IDialogueContext.cs │ │ ├── DummyCodeMachine.cs │ │ ├── IDialogueSet.cs │ │ ├── DialogueException.cs │ │ ├── IO │ │ │ ├── IExternalContextResolver.cs │ │ │ └── ISavestateFormat.cs │ │ ├── DialogueNotFoundException.cs │ │ ├── IDataRoot.cs │ │ ├── DialogueNodeNotFoundException.cs │ │ ├── DialogueSet.cs │ │ ├── IDataContext.cs │ │ ├── Symbol.cs │ │ ├── DummyCodeParser.cs │ │ ├── LocalDataContext.cs │ │ └── DataContext.cs │ ├── Parser │ │ ├── ParseError.cs │ │ ├── ErrorCode.cs │ │ ├── ConditionToken.cs │ │ ├── IExternalCodeParser.cs │ │ ├── ConditionTokenId.cs │ │ └── ExpressionOperator.cs │ ├── Samwise.sln │ └── Samwise.csproj ├── SamwiseWasm │ ├── LinkerConfig.xml │ ├── LocationInfo.cs │ ├── ParsedDialogueFile.cs │ ├── README.md │ ├── index.html │ ├── DebugInformation.cs │ ├── SamwiseWasm.csproj │ ├── wwwroot │ │ └── web.config │ ├── CodebaseDatabase.cs │ └── IDialogueUtils.cs ├── .vscode │ ├── launch.json │ └── tasks.json ├── SamwiseTest │ ├── SamwiseTest.csproj │ └── BaseTest.cs └── Samwise.sln ├── .gitignore └── LICENSE.md /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /vscode/build_package.bat: -------------------------------------------------------------------------------- 1 | call vsce package 2 | pause -------------------------------------------------------------------------------- /samples/multiFileTest2.sam: -------------------------------------------------------------------------------- 1 | § Multi2 2 | 3 | loise> And, this is Multi2! -------------------------------------------------------------------------------- /samples/multiFileTest1.sam: -------------------------------------------------------------------------------- 1 | § Multi1 2 | 3 | dave> Hey, this is Multi1 4 | -> Multi2 -------------------------------------------------------------------------------- /docs/logo/Logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/logo/Logo.psd -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [davidebarbieri] 4 | -------------------------------------------------------------------------------- /vscode/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon.png -------------------------------------------------------------------------------- /vscode/images/ss1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/ss1.png -------------------------------------------------------------------------------- /vscode/images/ss2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/ss2.png -------------------------------------------------------------------------------- /vscode/images/ss3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/ss3.png -------------------------------------------------------------------------------- /vscode/images/ss4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/ss4.png -------------------------------------------------------------------------------- /vscode/images/ss5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/ss5.png -------------------------------------------------------------------------------- /docs/logo/icon-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/logo/icon-black.png -------------------------------------------------------------------------------- /docs/images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/images/screenshot.jpg -------------------------------------------------------------------------------- /docs/logo/samwise-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/logo/samwise-black.png -------------------------------------------------------------------------------- /docs/logo/samwise-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/logo/samwise-white.png -------------------------------------------------------------------------------- /vscode/images/icon-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-data.png -------------------------------------------------------------------------------- /src/samwise.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /vscode/images/icon-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-package.png -------------------------------------------------------------------------------- /vscode/images/icon-player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-player.png -------------------------------------------------------------------------------- /vscode/images/icon-toolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-toolbox.png -------------------------------------------------------------------------------- /vscode/images/icon-transp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-transp.png -------------------------------------------------------------------------------- /docs/logo/samwise-both-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/docs/logo/samwise-both-large.png -------------------------------------------------------------------------------- /vscode/images/icon-light-transp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/images/icon-light-transp.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char1.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char2.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char3.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char4.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char5.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char6.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char7.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char8.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char9.png -------------------------------------------------------------------------------- /samples/.samwise/avatars/captain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/samples/.samwise/avatars/captain.png -------------------------------------------------------------------------------- /vscode/webview-ui/BadComic-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/BadComic-Italic.ttf -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char10.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char11.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char12.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char13.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char14.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char15.png -------------------------------------------------------------------------------- /vscode/webview-ui/avatars/char16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/avatars/char16.png -------------------------------------------------------------------------------- /vscode/webview-ui/gutterDialogue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/gutterDialogue.png -------------------------------------------------------------------------------- /vscode/webview-ui/BadComic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidebarbieri/samwise/HEAD/vscode/webview-ui/BadComic-Regular.ttf -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IAsyncCode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IAsyncCode {} 6 | } -------------------------------------------------------------------------------- /vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | src/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | **/*.bat -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ICheckableContent.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ICheckableContent: IContent, ICheckable 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /vscode/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/Examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Simple 4 | 5 | ## Choice 6 | 7 | ## Monkey Island-like Topics Tree 8 | 9 | ## Oxenfree-like 10 | 11 | ## Firewatch-like 12 | 13 | Fuzzy Pattern-Matching 14 | 15 | ## Villager NPC -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ITimeable.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ITimeable 6 | { 7 | bool HasTime(out double time); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ICase.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ICase: IContent 6 | { 7 | IMultiCaseNode Parent { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IStatement.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IStatement 6 | { 7 | void Execute(IDialogueContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IValue 6 | { 7 | void Traverse(System.Action action); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IConditional.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IConditional 6 | { 7 | IBoolValue Condition { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Parser/ParseError.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public struct ParseError 6 | { 7 | public string Error; 8 | public int Line; 9 | } 10 | } -------------------------------------------------------------------------------- /docs/Vscode_Advanced.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code extension: Advanced Features 2 | 3 | !> This page is under development 4 | 5 | ## Save/Load Variables State 6 | 7 | ## Generate Line IDs for Localization/Voice-over 8 | 9 | ## Export CSV lines for Localization -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IResolvableNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IResolvableNode 6 | { 7 | void Resolve(IDialogueNode destination); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Parser/ErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace Peevo.Samwise 2 | { 3 | public enum ErrorCode 4 | { 5 | Success, 6 | EmptyLabel, 7 | NoFirstLowerCase, 8 | NoFirstUpperCase, 9 | BadIndentation 10 | } 11 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IAdvanceableNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IAdvanceableNode 6 | { 7 | IDialogueNode Advance(IDialogueSet dialogues); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ICheckable.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ICheckable 6 | { 7 | bool HasCheck(out bool isPreCheck, out string checkName); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IIntegerValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IIntegerValue : IValue 6 | { 7 | long EvaluateInteger(IDialogueContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/ISymbolValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2023 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ISymbolValue : IValue 6 | { 7 | string EvaluateSymbol(IDialogueContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/VariableType.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public enum VariableType 6 | { 7 | Invalid, 8 | Bool, 9 | Integer, 10 | Symbol 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IAutoNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IAutoNode : IDialogueNode 6 | { 7 | IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Machine/IExternalCodeMachine.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IExternalCodeMachine 6 | { 7 | void Dispatch(IExternalCodeContext context, IAsyncCode asyncCode); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Machine/DialogueStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Peevo.Samwise 2 | { 3 | public enum DialogueStatus 4 | { 5 | Running, 6 | Stopping, 7 | Stopped, 8 | Waiting, 9 | Resolving, 10 | Challenging, 11 | ShowingSpeechOption 12 | } 13 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ISource.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ISource 6 | { 7 | Dialogue GetDialogue(); 8 | int SourceLineStart { get; } 9 | int SourceLineEnd { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /docs/Faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## Is Samwise completely free? 4 | No. You can use it for free, but it is mandatory to include it in the product credits. Check the License for details. 5 | 6 | ## Can you add the XOR operator to boolean expressions? 7 | No. We already have XOR at home. 8 | XOR at home: != -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IBoolValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IBoolValue: IValue 6 | { 7 | bool EvaluateBool(IDialogueContext context); 8 | void OnVisited(IDialogueContext context); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IMultiCaseNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public interface IMultiCaseNode 8 | { 9 | int CasesCount { get; } 10 | ICase GetCase(int i); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IReferencingNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IReferencingNode : IDialogueNode 6 | { 7 | string DestinationDialogueId { get; } 8 | string DestinationLabel { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/DummyCodeMachine.cs: -------------------------------------------------------------------------------- 1 | namespace Peevo.Samwise 2 | { 3 | public class DummyExternalCodeMachine : IExternalCodeMachine 4 | { 5 | public void Dispatch(IExternalCodeContext context, IAsyncCode asyncCode) 6 | { 7 | context.OnEnd(); 8 | } 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/IDialogueSet.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IDialogueSet 6 | { 7 | bool GetDialogue(string dialogueName, out Dialogue dialogue); 8 | IDialogueNode GetNodeFromLabel(string dialogueId, string label); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ITextContent.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | // Piece of data that provide text (like a caption, a speech node or an option). 6 | // It's what you'll probably localize. 7 | public interface ITextContent : IContent 8 | { 9 | string Text { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Machine/IExternalCodeContext.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IExternalCodeContext 6 | { 7 | long Uid { get; } 8 | 9 | IDialogueContext Parent { get; } 10 | 11 | // Call this when the external code is ended 12 | void OnEnd(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IContent.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | // A taggable and checkable part of a node 6 | public interface IContent: ISource, ITaggable, IConditional 7 | { 8 | string PrintLine(string indentationUnit); 9 | string GenerateUidPreamble(Dialogue dialogue); 10 | } 11 | } -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](logo/icon-black.png) 2 | 3 | # Samwise Dialogue System 4 | 5 | > Your best ally on the journey to craft captivating narrative realms 6 | 7 | - Expressive and powerful language 8 | - Concise and readable syntax 9 | - Fast and lightweight runtime 10 | 11 | [Home](Home.md) 12 | [Quick Start](Quick_Start.md) 13 | [GitHub](https://github.com/davidebarbieri/samwise/) 14 | -------------------------------------------------------------------------------- /src/Samwise/Parser/ConditionToken.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | internal struct ConditionToken 6 | { 7 | public ConditionTokenId id; 8 | public int position; 9 | public int length; 10 | 11 | public string GetText(string fullText) => fullText.Substring(position, length); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IUnaryOperationValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IUnaryOperationBoolValue : IBoolValue 6 | { 7 | IBoolValue A { get; set; } 8 | } 9 | 10 | public interface IUnaryOperationIntegerValue : IIntegerValue 11 | { 12 | IIntegerValue A { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/DialogueException.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | [System.Serializable] 6 | public class DialogueException : System.Exception 7 | { 8 | public IDialogueContext Context; 9 | public DialogueException(IDialogueContext context, string message) : base(message) { Context = context; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/IO/IExternalContextResolver.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IExternalContextSaveResolver 6 | { 7 | string GetUIDFromObject(object context); 8 | } 9 | 10 | public interface IExternalContextLoadResolver 11 | { 12 | object GetObjectFromUID(string uid); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ExitNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class ExitNode : LocalGotoNode 6 | { 7 | public ExitNode(int sourceLine) : base(sourceLine, null, null) {} 8 | 9 | public override string PrintPayload() 10 | { 11 | return "-> end"; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IBlockContainerNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IBlockContainerNode : IDialogueNode 6 | { 7 | int ChildrenCount { get; } 8 | IDialogueBlock GetChild(int i); 9 | string LookUpChildBlockTabsPrefix(IDialogueBlock childBlock, string indentationUnit); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/NextNodeBehaviour.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public enum NextBlockPolicy 6 | { 7 | ParentNext, // next node is parent's next (default) 8 | ReturnToParent, // next node is this block's parent node (e.g. <-) 9 | End // there's no next node (e.g. anonymous blocks) 10 | } 11 | } -------------------------------------------------------------------------------- /src/SamwiseWasm/LinkerConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/AnonymousSequenceBlock.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class AnonymousSequenceBlock : SequenceBlock 6 | { 7 | public override NextBlockPolicy NextBlockPolicy => NextBlockPolicy.End; 8 | 9 | public AnonymousSequenceBlock(IBlockContainerNode parent) : base(parent) {} 10 | } 11 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IOption.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IOption : ICase, ITextContent, ICheckableContent, ITimeable 6 | { 7 | bool MuteOption { get; } 8 | bool ReturnOption { get; } 9 | 10 | bool IsAvailable(IDialogueContext context); 11 | 12 | IDialogueBlock Block { get; } 13 | new IChoosableNode Parent { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /vscode/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IStatefulElement.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | // Piece of data that provide text (like a caption, a speech node or an option). 6 | // It's what you'll probably localize. 7 | public interface IStatefulElement 8 | { 9 | string StateVariableName { get; set; } 10 | string StateVariableContext { get; set; } 11 | bool UsesAnonymousVariable { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /vscode/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /src/SamwiseWasm/LocationInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Peevo.Samwise.Wasm 5 | { 6 | public struct LocationInfo 7 | { 8 | public string file; 9 | public int lineStart; 10 | public int lineEnd; 11 | 12 | public LocationInfo(string file, int lineStart, int lineEnd) 13 | { 14 | this.file = file; 15 | this.lineStart = lineStart; 16 | this.lineEnd = lineEnd; 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/DialogueNotFoundException.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | [System.Serializable] 6 | public class DialogueNotFoundException : DialogueException 7 | { 8 | public IReferencingNode CausingNode; 9 | 10 | public DialogueNotFoundException(IDialogueContext context, IReferencingNode causingNode) : base(context, $"Dialogue {causingNode.DestinationDialogueId} not found.") { CausingNode = causingNode; } 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /src/Samwise/Parser/IExternalCodeParser.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | // Optional external code parser 6 | public interface IExternalCodeParser 7 | { 8 | bool Parse(string code, out IStatement statement, System.Action logError); 9 | bool ParseCondition(string code, out IBoolValue expression, System.Action logError); 10 | bool ParseAsync(string code, out IAsyncCode asyncCode, System.Action logError); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IChoosableNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public interface IChoosableNode: IDialogueNode 8 | { 9 | int OptionsCount { get; } 10 | Option GetOption(int index); 11 | 12 | IEnumerable GetAvailableOptions(IDialogueContext context); 13 | string CharacterId { get; } 14 | double? Time { get; } 15 | 16 | IDialogueNode Next(IOption choice, IDialogueContext context); 17 | } 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /src/obj 2 | /src/bin 3 | /Samwise Player/Library 4 | /Samwise Player/Temp 5 | /Samwise Player/obj/Debug 6 | /Samwise Player/Logs 7 | /Samwise Player/.vs 8 | /src/Samwise/obj 9 | /src/SamwiseTest/bin/Debug 10 | /src/SamwiseTest/obj/Debug 11 | /src/Samwise/bin/Debug/netstandard2.0 12 | /bin/Debug 13 | /obj/Debug 14 | /src/SamwiseTest/obj 15 | /vscode/out/app 16 | /bin/Release 17 | /obj/Release 18 | /src/SamwiseWasm/obj/Debug 19 | /src/SamwiseWasm/obj 20 | /vscode/out 21 | /vscode/node_modules 22 | /src/SamwiseTest/bin/release/net6.0 23 | /obj 24 | /bin 25 | /src/.vs 26 | *.vsix 27 | -------------------------------------------------------------------------------- /src/SamwiseWasm/ParsedDialogueFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Peevo.Samwise.Wasm 5 | { 6 | public class ParsedDialogueFile 7 | { 8 | public string Filename; 9 | public readonly DebugInformation DebugInformation = new DebugInformation(); 10 | 11 | public ParsedDialogueFile(string filename) 12 | { 13 | Filename = filename; 14 | } 15 | 16 | public void AddDialogue(Dialogue dialogue) 17 | { 18 | DebugInformation.Gather(dialogue); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/IO/ISavestateFormat.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | using System.IO; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public interface ISavestateFormat 8 | { 9 | int Version { get; } 10 | 11 | bool SaveState(BinaryWriter writer, DialogueMachine machine, IExternalContextSaveResolver externalContextResolver = null); 12 | bool LoadState(BinaryReader reader, DialogueMachine machine, System.Func onUnresolvedDialogue, IExternalContextLoadResolver externalContextResolver = null); 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /docs/Games.md: -------------------------------------------------------------------------------- 1 | # Games Using Samwise 2 | 3 | In this page, I'll gather games that utilize the Samwise Dialogue System. If you've released a video game that employs Samwise and would like to be featured in this list, please don't hesitate to reach out to me. I would be more than happy to add you. 4 | 5 | Please feel free to explore the games showcased here and see how the Samwise Dialogue System enhances the interactive narrative experience in each one. 6 | 7 | ## Upcoming 8 | - **[Unannounced project]** from *The Pixel Hunt* 9 | 10 | ## Released 11 | - For now, no games... I hope this list grows soon!! 12 | -------------------------------------------------------------------------------- /vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2020" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vscode/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run xUnit Tests", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "preLaunchTask": "build", 9 | "program": "dotnet", 10 | "args": [ 11 | "test", 12 | "${workspaceFolder}/SamwiseTest/SamwiseTest.csproj" 13 | ], 14 | "cwd": "${workspaceFolder}", 15 | "console": "internalConsole", 16 | "stopAtEntry": false, 17 | "internalConsoleOptions": "openOnSessionStart" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/IDataRoot.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | using System.Collections.Generic; 3 | 4 | namespace Peevo.Samwise 5 | { 6 | public interface IDataRoot 7 | { 8 | IDataContext CreateData(IDialogueContext context); 9 | void ReleaseData(IDataContext data); 10 | IDataContext LookupDataContext(string contextName); 11 | IDataContext LookupOrCreateDataContext(string contextName); 12 | int GetRandom(); 13 | void Clear(); 14 | 15 | IEnumerable<(string, IDataContext)> GetDataContexes(); 16 | IList GetSubcontexes(IDataContext context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Samwise/Parser/ConditionTokenId.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | internal enum ConditionTokenId 6 | { 7 | RoundOpen, 8 | RoundClose, 9 | Or, 10 | And, 11 | Not, 12 | Add, 13 | Sub, 14 | Mul, 15 | Div, 16 | Equal, 17 | Different, 18 | LEqual, 19 | Less, 20 | GEqual, 21 | Greater, 22 | IntegerVar, 23 | BoolVar, 24 | SymbolVar, 25 | IntegerConst, 26 | SymbolConst, 27 | True, 28 | False, 29 | Once, 30 | External 31 | } 32 | } -------------------------------------------------------------------------------- /docs/Acknowledgements.md: -------------------------------------------------------------------------------- 1 | 2 | # Acknowledgements 3 | 4 | I would like to express my gratitude to **Florent Maurin** for being the very first user of Samwise, for placing his trust in me by letting me integrate my tool in one of his video games even if it was the first game doing so, and for providing me with invaluable feedback that has undeniably enhanced the quality of this tool for everyone. 5 | 6 | --- 7 | 8 | While Samwise is fundamentally a text-based tool, I must acknowledge that the design of Samwise was consciously and/or subconsciously influenced by using [Outspoken](https://www.demigiant.com/plugins/outspoken/), a brilliant node-based visual editor made by the awesome **Daniele 'Demigiant' Giardini**. -------------------------------------------------------------------------------- /src/SamwiseWasm/README.md: -------------------------------------------------------------------------------- 1 | Samwise WASM allows to run the Samwise interpreter on a WebAssembly environment. 2 | 3 | It is based on the Uno.Wasm.Bootstrap package in order to package the C# .NET code, 4 | and run it from a compatible browser environment or node.js; 5 | and the Uno.Foundation.Runtime.WebAssembly package in order to execute javascript from C#. 6 | 7 | # Compilation 8 | 9 | In order to compile this extension you'll need .Net 6.0 SDK. 10 | 11 | Then, run the following command within the terminal, from this folder: 12 | dotnet build -c Release 13 | 14 | the command will create the "out/app" folder inside the "vscode" directory 15 | (which is the project for the Visual Studio Code extension). -------------------------------------------------------------------------------- /src/Samwise/Runtime/DialogueNodeNotFoundException.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | [System.Serializable] 6 | public class DialogueNodeNotFoundException : DialogueException 7 | { 8 | public string DialogueId; 9 | public string Label; 10 | 11 | public DialogueNodeNotFoundException(IDialogueContext context, string dialogueId, string label) 12 | : base(context, $"Label {dialogueId}.{label} not found.") { DialogueId = dialogueId; Label = label; } 13 | 14 | public override string ToString() 15 | { 16 | return "Unable to find node reference (" + DialogueId + "." + Label + ")"; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/JoinNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class JoinNode : DialogueNode, IAutoNode 6 | { 7 | public string BranchName; 8 | 9 | public JoinNode(int sourceLine, string branchName) : base(sourceLine, sourceLine) 10 | { 11 | BranchName = branchName; 12 | } 13 | 14 | public IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 15 | { 16 | return this.FindNextSibling(); 17 | } 18 | 19 | public override string PrintPayload() 20 | { 21 | return (string.IsNullOrEmpty(BranchName) ? "" : BranchName + " ") + "<="; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/StatefulSelectionNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public abstract class StatefulSelectionNode : SelectionNode, IStatefulElement 6 | { 7 | public string StateVariableName { get; set; } 8 | public string StateVariableContext { get; set; } 9 | public bool UsesAnonymousVariable { get; set; } 10 | 11 | public StatefulSelectionNode(int sourceLine, string context, string counterVariable, bool usesAnonymousVariable) : base(sourceLine) 12 | { 13 | StateVariableContext = context; 14 | StateVariableName = counterVariable; 15 | UsesAnonymousVariable = usesAnonymousVariable; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/CancelNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class CancelNode : DialogueNode, IAutoNode 6 | { 7 | public string BranchName; 8 | 9 | public CancelNode(int sourceLine, string branchName) : base(sourceLine, sourceLine) 10 | { 11 | BranchName = branchName; 12 | } 13 | 14 | public IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 15 | { 16 | return this.FindNextSibling(); 17 | } 18 | 19 | public override string PrintPayload() 20 | { 21 | return (string.IsNullOrEmpty(BranchName) ? "" : BranchName + " ") + " evaluator; 11 | public System.Func allocator; 12 | 13 | public ExpressionOperator(ConditionTokenId token, int leftPriority, int rightPriority, System.Func evaluator, System.Func allocator) 14 | { 15 | this.token = token; 16 | this.leftPriority = leftPriority; 17 | this.rightPriority = rightPriority; 18 | this.evaluator = evaluator; 19 | this.allocator = allocator; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /docs/logo/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | Logo 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/CaptionNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class CaptionNode : DialogueNode, IAdvanceableNode, ITextContent 6 | { 7 | public string Text { get; set; } 8 | 9 | public CaptionNode(int sourceLineStart, int sourceLineEnd, string text) : base(sourceLineStart, sourceLineEnd) 10 | { 11 | Text = text; 12 | } 13 | 14 | public IDialogueNode Advance(IDialogueSet dialogues) 15 | { 16 | return this.FindNextSibling(); 17 | } 18 | 19 | public override string PrintPayload() 20 | { 21 | return "* " + Text.Replace("\n", "↵\n").Replace("#", "##"); 22 | } 23 | 24 | public override string GenerateUidPreamble(Dialogue dialogue) 25 | { 26 | return dialogue.Label + "_Caption_"; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/NegateValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class NegateValue : IIntegerValue, IUnaryOperationIntegerValue 6 | { 7 | public IIntegerValue A { get; set; } 8 | 9 | public NegateValue() 10 | { 11 | A = null; 12 | } 13 | 14 | public NegateValue(IIntegerValue a) 15 | { 16 | A = a; 17 | } 18 | 19 | public long EvaluateInteger(IDialogueContext context) 20 | { 21 | return -A.EvaluateInteger(context); 22 | } 23 | 24 | public void Traverse(System.Action action) 25 | { 26 | action.Invoke(this); 27 | A.Traverse(action); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return "-" + A.ToString(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Samwise Docs 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Loading documentation...
13 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "brackets": [ 3 | ["{", "}"], 4 | ["[", "]"], 5 | ["(", ")"] 6 | ], 7 | "autoClosingPairs": [ 8 | { "open": "[", "close": "]" }, 9 | { "open": "{", "close": "}" }, 10 | { "open": "(", "close": ")" }, 11 | { "open": "\"", "close": "\"", "notIn": ["string"] } 12 | ], 13 | "surroundingPairs": [ 14 | ["{", "}"], 15 | ["[", "]"], 16 | ["(", ")"], 17 | ["\"", "\""] 18 | ], 19 | "colorizedBracketPairs": [ 20 | ], 21 | "onEnterRules": [ 22 | { 23 | "beforeText": "^\\s*(?:>>>?|><|\\?|!!?|%|\\$).*$", 24 | "action": { "indent": "indent", "appendText": "- " } 25 | }, 26 | { 27 | "beforeText": "^[^>\\*]*:\\s*(?:#.*)?$", 28 | "action": { "indent": "indent", "appendText": "- " } 29 | }, 30 | { 31 | "beforeText": "^[^>\\*]*(?:=>)\\s*(?:#.*)?$", 32 | "action": { "indent": "indent"} 33 | }, 34 | { 35 | "beforeText": "^\\s*(?:]).*)?$", 36 | "action": { "indent": "indent" } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/FallbackNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class FallbackNode : SelectionNode 6 | { 7 | public FallbackNode(int sourceLine) : base(sourceLine) {} 8 | 9 | public override IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 10 | { 11 | for (int i = 0; i < ChildrenCount; ++i) 12 | { 13 | var ccase = GetChild(i); 14 | 15 | if (ccase.Condition != null && !ccase.Condition.EvaluateBool(context)) 16 | continue; 17 | 18 | ccase.Condition?.OnVisited(context); 19 | 20 | if (ccase.ChildrenCount > 0) 21 | return ccase.GetChild(0); 22 | break; 23 | } 24 | 25 | return this.FindNextSibling(); 26 | } 27 | 28 | public override string PrintPayload() 29 | { 30 | return "?"; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Home](Home.md) 2 | 3 | * [Quick Start](Quick_Start.md) 4 | 5 | * Writing in Samwise 6 | * [Basics](Language_Basics.md) 7 | * [Variables and Flow Control](Language_Logic.md) 8 | * [Selection Nodes](Language_Selection.md) 9 | * [Parallel Dialogues](Language_Parallel.md) 10 | * [Challenges](Language_Challenge.md) 11 | * [Metadata](Language_Metadata.md) 12 | * [Code Extensions](Language_CodeExtensions.md) 13 | 14 | * Visual Studio Code Extension 15 | * [Writing Dialogues](Vscode_Write.md) 16 | * [Advances Features](Vscode_Advanced.md) 17 | 18 | * The Samwise Language 19 | * [Language Specification](Language.md) 20 | * [Design Principles](Design.md) 21 | 22 | * Samwise Runtime 23 | * [C# Runtime Library](Runtime.md) 24 | * [Unity Plug-In](Plugin_Unity.md) 25 | * Adding Custom Code Extensions 26 | 27 | * More 28 | * [Changelog](Changelog.md) 29 | * [TO-DO](Todo.md) 30 | 31 | * [Acknowledgements](Acknowledgements.md) -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/NotValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class NotValue : IBoolValue, IUnaryOperationBoolValue 6 | { 7 | public IBoolValue A { get; set; } 8 | 9 | public NotValue() 10 | { 11 | A = null; 12 | } 13 | 14 | public NotValue(IBoolValue a) 15 | { 16 | A = a; 17 | } 18 | 19 | public bool EvaluateBool(IDialogueContext context) 20 | { 21 | return !A.EvaluateBool(context); 22 | } 23 | 24 | public void OnVisited(IDialogueContext context) 25 | { 26 | A.OnVisited(context); 27 | } 28 | 29 | public void Traverse(System.Action action) 30 | { 31 | action.Invoke(this); 32 | A.Traverse(action); 33 | } 34 | 35 | public override string ToString() 36 | { 37 | return "!" + A.ToString(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ITaggable.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface ITaggable 6 | { 7 | ITagData TagData { get; set; } 8 | } 9 | 10 | public static class ITaggableMethods 11 | { 12 | public static bool HasTag(this ITaggable taggable, string tag) 13 | { 14 | if (taggable.TagData == null) 15 | return false; 16 | 17 | return taggable.TagData.HasTag(tag); 18 | } 19 | 20 | public static string GetID(this ITaggable taggable) 21 | { 22 | if (taggable.TagData == null) 23 | return null; 24 | 25 | return taggable.TagData.GetTagValue("id"); 26 | } 27 | 28 | public static void SetID(this ITaggable taggable, string id) 29 | { 30 | if (taggable.TagData == null) 31 | taggable.TagData = new TagData(); 32 | 33 | taggable.TagData.AddTag("id", id); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/SpeechNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class SpeechNode : DialogueNode, IAdvanceableNode, ITextContent 6 | { 7 | public string CharacterId; 8 | public string Text { get; set; } 9 | 10 | public SpeechNode(int sourceLineStart, int sourceLineEnd, string characterId, string text) : base(sourceLineStart, sourceLineEnd) 11 | { 12 | CharacterId = characterId; 13 | Text = text; 14 | } 15 | 16 | public IDialogueNode Advance(IDialogueSet dialogues) 17 | { 18 | return this.FindNextSibling(); 19 | } 20 | 21 | public override string PrintPayload() 22 | { 23 | return CharacterId + "> " + Text.Replace("\n", "↵\n").Replace("#", "##"); 24 | } 25 | 26 | public override string GenerateUidPreamble(Dialogue dialogue) 27 | { 28 | return dialogue.Label + "_" + CharacterId + "_"; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/SamwiseWasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | $(ADDITIONAL_CSS) 11 | $(ADDITIONAL_HEAD) 12 | 13 | 14 |
15 |
18 | 19 | 22 | 23 | 24 | 25 |
26 |
27 |

28 | 31 | 32 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/CheckResultBlock.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class CheckResultBlock : SequenceBlock 6 | { 7 | public ITagData TagData { get; set; } 8 | public bool Pass { get; private set; } 9 | 10 | public new CheckNode Parent => (CheckNode)base.Parent; 11 | 12 | public CheckResultBlock(bool pass, CheckNode parent, ITagData tagData) : base(parent) 13 | { 14 | Pass = pass; 15 | TagData = tagData; 16 | } 17 | 18 | public override string PrintSubtree(string indentationPrefix, string indentationUnit) 19 | { 20 | var s = indentationPrefix + PrintPayload() + DialogueNode.GetTagsString(TagData); 21 | 22 | if (base.ChildrenCount > 0) 23 | s += "\n" + base.PrintSubtree(indentationUnit + indentationPrefix, indentationUnit); 24 | 25 | return s; 26 | } 27 | 28 | public string PrintPayload() 29 | { 30 | var s = Pass ? "+" : "-"; 31 | 32 | return s; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/SamwiseTest/SamwiseTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {be0c52a1-182e-4fe2-abc5-c724a589613e} 26 | Samwise 27 | 28 | 29 | 30 | 31 | PreserveNewest 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Samwise/Samwise.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.5.2.0 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samwise", "Samwise.csproj", "{31E02926-6B0F-C4E0-DC29-53E9BE4A1D0D}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {31E02926-6B0F-C4E0-DC29-53E9BE4A1D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {31E02926-6B0F-C4E0-DC29-53E9BE4A1D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {31E02926-6B0F-C4E0-DC29-53E9BE4A1D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {31E02926-6B0F-C4E0-DC29-53E9BE4A1D0D}.Release|Any CPU.Build.0 = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(SolutionProperties) = preSolution 19 | HideSolutionNode = FALSE 20 | EndGlobalSection 21 | GlobalSection(ExtensibilityGlobals) = postSolution 22 | SolutionGuid = {663A2F1B-411C-41C4-85A8-E40EEF7B5523} 23 | EndGlobalSection 24 | EndGlobal 25 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IncrementAssignmentStatement.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class IncrementAssignmentStatement : IntegerAssignmentStatement 6 | { 7 | public override void Execute(IDialogueContext context) 8 | { 9 | var dataContext = context.LookupOrCreateDataContext(Context); 10 | 11 | dataContext.SetValueInt(Name, dataContext.GetValueInt(Name) + Value.EvaluateInteger(context)); 12 | } 13 | 14 | public override string ToString() 15 | { 16 | return Context + Name + " += " + Value.ToString(); 17 | } 18 | } 19 | public class DecrementAssignmentStatement : IntegerAssignmentStatement 20 | { 21 | public override void Execute(IDialogueContext context) 22 | { 23 | var dataContext = context.LookupOrCreateDataContext(Context); 24 | 25 | dataContext.SetValueInt(Name, dataContext.GetValueInt(Name) - Value.EvaluateInteger(context)); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return Context + Name + " -= " + Value.ToString(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/WaitNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class WaitTimeNode : DialogueNode, IAdvanceableNode 6 | { 7 | public double Time; 8 | 9 | public WaitTimeNode(int sourceLine, double time) : base(sourceLine, sourceLine) 10 | { 11 | Time = time; 12 | } 13 | 14 | public IDialogueNode Advance(IDialogueSet dialogues) 15 | { 16 | return this.FindNextSibling(); 17 | } 18 | 19 | public override string PrintPayload() 20 | { 21 | return "{ " + "wait " + Time.ToString(System.Globalization.CultureInfo.InvariantCulture) + "s" + " }"; 22 | } 23 | } 24 | 25 | public class WaitExpressionNode : DialogueNode 26 | { 27 | public IBoolValue Expression; 28 | 29 | public WaitExpressionNode(int sourceLine, IBoolValue expression) : base(sourceLine, sourceLine) 30 | { 31 | Expression = expression; 32 | } 33 | 34 | public override string PrintPayload() 35 | { 36 | return "{ " + "wait " + Expression.ToString() + " }"; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/DialogueSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Peevo.Samwise 4 | { 5 | // Sample IDialogueSet implementation 6 | public class DialogueSet : IDialogueSet 7 | { 8 | public DialogueSet() {} 9 | 10 | public DialogueSet(IList dialogues) 11 | { 12 | AddDialogues(dialogues); 13 | } 14 | 15 | public void AddDialogue(Dialogue dialogue) 16 | { 17 | symbolMap[dialogue.Label] = dialogue; 18 | } 19 | 20 | public void AddDialogues(IList dialogues) 21 | { 22 | for (int i=0, count=dialogues.Count; i symbolMap = new Dictionary(); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/SingleBlock.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class SingleBlock : IDialogueBlock 6 | { 7 | public virtual NextBlockPolicy NextBlockPolicy => NextBlockPolicy.ParentNext; 8 | public IBlockContainerNode Parent { get; private set; } 9 | public int ChildrenCount => 1; 10 | public IDialogueNode GetChild(int i) => child; 11 | 12 | public SingleBlock(IBlockContainerNode parent) { Parent = parent; } 13 | 14 | public void PushChild(IDialogueNode node) 15 | { 16 | node.Block = this; 17 | node.BlockId = 0; 18 | 19 | child = node; 20 | } 21 | 22 | public void PopChild() 23 | { 24 | child = null; 25 | } 26 | 27 | public int GetNextIndex(int i) 28 | { 29 | return -1; 30 | } 31 | 32 | public virtual string PrintSubtree(string indentationPrefix, string indentationUnit) 33 | { 34 | return child.PrintSubtree(indentationPrefix, indentationUnit); 35 | } 36 | 37 | public string LookUpChildNodeTabsPrefix(IDialogueNode childNode, string indentationUnit) 38 | { 39 | // same indentation as block 40 | return this.LookUpTabsPrefix(indentationUnit); 41 | } 42 | 43 | IDialogueNode child; 44 | } 45 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/IDataContext.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | using System.Collections.Generic; 3 | 4 | namespace Peevo.Samwise 5 | { 6 | public interface IDataContext 7 | { 8 | string Name { get; } 9 | 10 | event System.Action onBoolDataChanged; 11 | event System.Action onIntDataChanged; 12 | event System.Action onSymbolDataChanged; 13 | event System.Action onDataClear; 14 | event System.Action onClear; 15 | 16 | int BoolVariablesCount { get; } 17 | int IntVariablesCount { get; } 18 | int SymbolVariablesCount { get; } 19 | 20 | bool GetValueBool(string name, bool defaultValue = false); 21 | void SetValueBool(string name, bool value); 22 | void ClearValueBool(string name); 23 | long GetValueInt(string name, long defaultValue = 0); 24 | void SetValueInt(string name, long value); 25 | void ClearValueInt(string name); 26 | string GetValueSymbol(string name, string defaultValue = null); 27 | void SetValueSymbol(string name, string value); 28 | void ClearValueSymbol(string name); 29 | 30 | IEnumerable<(string, bool)> GetBoolVariables(); 31 | IEnumerable<(string, long)> GetIntVariables(); 32 | IEnumerable<(string, string)> GetSymbolVariables(); 33 | 34 | void Clear(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/IntegerValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class IntegerVariableValue : IIntegerValue 6 | { 7 | public string Name; 8 | public string Context; 9 | 10 | public IntegerVariableValue(string name, string context) 11 | { 12 | Name = name; 13 | Context = context != null ? context : ""; 14 | } 15 | 16 | public long EvaluateInteger(IDialogueContext context) 17 | { 18 | return context.LookupDataContext(Context)?.GetValueInt(Name) ?? 0; 19 | } 20 | 21 | public void Traverse(System.Action action) { action.Invoke(this); } 22 | 23 | public override string ToString() 24 | { 25 | return Context + Name; 26 | } 27 | } 28 | 29 | public class IntegerConstantValue : IIntegerValue 30 | { 31 | public long Value; 32 | 33 | public IntegerConstantValue(long value) 34 | { 35 | Value = value; 36 | } 37 | 38 | public long EvaluateInteger(IDialogueContext dataContext) 39 | { 40 | return Value; 41 | } 42 | 43 | public void Traverse(System.Action action) { action.Invoke(this); } 44 | 45 | public override string ToString() 46 | { 47 | return Value.ToString(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/CodeNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class CodeNode : DialogueNode, IAutoNode 6 | { 7 | 8 | public IStatement Statement; 9 | 10 | public CodeNode(int sourceLine, IStatement statement) : base(sourceLine, sourceLine) 11 | { 12 | Statement = statement; 13 | } 14 | 15 | public IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 16 | { 17 | Statement.Execute(context); 18 | return this.FindNextSibling(); 19 | } 20 | 21 | public override string PrintPayload() 22 | { 23 | return "{ " + Statement.ToString() + " }"; 24 | } 25 | } 26 | 27 | public class ExternalCodeNode : DialogueNode, IAutoNode 28 | { 29 | 30 | public IStatement Statement; 31 | 32 | public ExternalCodeNode(int sourceLine, IStatement statement) : base(sourceLine, sourceLine) 33 | { 34 | Statement = statement; 35 | } 36 | 37 | public IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 38 | { 39 | Statement.Execute(context); 40 | return this.FindNextSibling(); 41 | } 42 | 43 | public override string PrintPayload() 44 | { 45 | return "{{ " + Statement.ToString() + " }}"; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /docs/Language_CodeExtensions.md: -------------------------------------------------------------------------------- 1 | # Code Extensions 2 | 3 | !> [ This page is under development ] 4 | 5 | ### Embeddable Code (Custom-defined code) 6 | 7 | Embedded code nodes are similar to built-in code nodes, but they use double curly brackets. 8 | 9 | !> In order to support this kind of node, the game must provide a Custom Code Parser using the Runtime API. 10 | 11 | #### Code statements 12 | 13 | Such statements are executed synchonously. 14 | 15 | ``` 16 | {{ external code }} 17 | ``` 18 | 19 | #### Embeddable Condition 20 | 21 | The API allows the user to define conditions in their custom language as well. 22 | 23 | ``` 24 | [{{ external code }}] 25 | ``` 26 | 27 | #### Fork/Join/Await on Embeddable Code 28 | 29 | In case of asynchonous code, the following syntax must be used: 30 | 31 | ``` 32 | => {{ code }} 33 | name => {{ code }} 34 | <=> {{ code }} 35 | ``` 36 | forking, joining or awaiting embeddable code is similar to what happens with regular fork/join/await nodes. 37 | The difference is that instead of executing other dialogues, custom code will be issues asynchronously. 38 | 39 | 40 | 41 | ## Fork/Join/Await on Embeddable Code 42 | 43 | In case of asynchonous code, the following syntax must be used: 44 | 45 | ``` 46 | => {{ code }} 47 | name => {{ code }} 48 | <=> {{ code }} 49 | ``` 50 | forking, joining or awaiting embeddable code is similar to what happens with regular fork/join/await nodes. 51 | The difference is that instead of executing other dialogues, custom code will be issues asynchronously. -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/AndValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class AndValue : IBoolValue, IBinaryOperationBoolValue 6 | { 7 | public IBoolValue A { get; set; } 8 | public IBoolValue B { get; set; } 9 | 10 | public AndValue() 11 | { 12 | A = null; 13 | B = null; 14 | } 15 | 16 | public AndValue(IBoolValue a, IBoolValue b) 17 | { 18 | A = a; 19 | B = b; 20 | } 21 | 22 | public bool EvaluateBool(IDialogueContext context) 23 | { 24 | return A.EvaluateBool(context) && B.EvaluateBool(context); 25 | } 26 | 27 | public IBinaryOperationValue Overload(IValue a, IValue b) 28 | { 29 | A = a as IBoolValue; 30 | B = b as IBoolValue; 31 | return (A != null && B != null) ? this : null; 32 | } 33 | 34 | 35 | public void OnVisited(IDialogueContext context) 36 | { 37 | A.OnVisited(context); 38 | B.OnVisited(context); 39 | } 40 | 41 | public void Traverse(System.Action action) 42 | { 43 | action.Invoke(this); 44 | A.Traverse(action); 45 | B.Traverse(action); 46 | } 47 | 48 | public override string ToString() 49 | { 50 | return "(" + A.ToString() + " & " + B.ToString() + ")"; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/OrValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class OrCondition : IBoolValue, IBinaryOperationBoolValue 6 | { 7 | public IBoolValue A { get; set; } 8 | public IBoolValue B { get; set; } 9 | 10 | public OrCondition() 11 | { 12 | A = null; 13 | B = null; 14 | } 15 | 16 | public OrCondition(IBoolValue a, IBoolValue b) 17 | { 18 | A = a; 19 | B = b; 20 | } 21 | 22 | public bool EvaluateBool(IDialogueContext context) 23 | { 24 | return A.EvaluateBool(context) || B.EvaluateBool(context); 25 | } 26 | 27 | public IBinaryOperationValue Overload(IValue a, IValue b) 28 | { 29 | A = a as IBoolValue; 30 | B = b as IBoolValue; 31 | return (A != null && B != null) ? this : null; 32 | } 33 | 34 | 35 | public void OnVisited(IDialogueContext context) 36 | { 37 | A.OnVisited(context); 38 | B.OnVisited(context); 39 | } 40 | 41 | public void Traverse(System.Action action) 42 | { 43 | action.Invoke(this); 44 | A.Traverse(action); 45 | B.Traverse(action); 46 | } 47 | 48 | public override string ToString() 49 | { 50 | return "(" + A.ToString() + " | " + B.ToString() + ")"; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /docs/Language_Metadata.md: -------------------------------------------------------------------------------- 1 | # Metadata 2 | 3 | !> [ This page is under development ] 4 | 5 | ## Metadata: Tags 6 | 7 | Meaningful metadata can be incorporated into dialogue nodes by utilizing tags. 8 | 9 | ```samwise 10 | character> This is a random line # tag1, tag2, "This is a complex tag 1", "complex tag 2" 11 | ``` 12 | 13 | Tags, such as #happy and #skippable, serve as markers that can be leveraged by the game's code to implement tailored behaviors associated with specific nodes. For instance, these tags may trigger unique character reactions or prompt specific in-game events. 14 | 15 | Additionally, tags offer a valuable space for insights related to a dialogue line. Use double quoted tags to provide voiceover tips, offer guidance for effective delivery, or even explain the nuances of a particular word to assist translators. 16 | 17 | Tags can be added to Dialogue Titles block too: 18 | ```samwise 19 | § Title # tag1, tag2, "Comment" 20 | ``` 21 | To be honest, tags can be added to virtually everything, including options. 22 | 23 | It's also possible to assign names to tags, allowing for better specialization or even just making them easier to track via code. Here is the syntax: 24 | ```samwise 25 | character> Where are my panties?! # normal_tag, face=angry, voice_comment="The character here is very upset" 26 | ``` 27 | ### Node Identifier 28 | 29 | There's a unique named tag titled **"id"** designated as the default identifier for each node. This identifier is essential for tracking the corresponding line in a different language or locating the voiceover audio file for that line. -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/BoolValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class BoolVariableValue : IBoolValue 6 | { 7 | public string Name; 8 | public string Context; 9 | 10 | public BoolVariableValue(string name, string context) 11 | { 12 | Name = name; 13 | Context = context != null ? context : ""; 14 | } 15 | 16 | public bool EvaluateBool(IDialogueContext context) 17 | { 18 | return context.LookupDataContext(Context)?.GetValueBool(Name) ?? false; 19 | } 20 | 21 | public void OnVisited(IDialogueContext context) {} 22 | 23 | public void Traverse(System.Action action) { action.Invoke(this); } 24 | 25 | public override string ToString() 26 | { 27 | return Context + Name; 28 | } 29 | } 30 | 31 | public class BoolConstantValue : IBoolValue 32 | { 33 | public bool Value; 34 | 35 | public BoolConstantValue(bool value) 36 | { 37 | Value = value; 38 | } 39 | 40 | public bool EvaluateBool(IDialogueContext context) 41 | { 42 | return Value; 43 | } 44 | 45 | public void OnVisited(IDialogueContext context) {} 46 | public void Traverse(System.Action action) { action.Invoke(this); } 47 | 48 | public override string ToString() 49 | { 50 | return Value ? "true" : "false"; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Samwise/Samwise.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | netstandard2.0 6 | Davide 'PeevishDave' Barbieri 7 | true 8 | Copyright 2022 Davide 'PeevishDave' Barbieri. 9 | true 10 | true 11 | true 12 | 13 | 14 | 15 | ..\..\bin\Debug 16 | ..\..\obj\Debug 17 | false 18 | false 19 | false 20 | 21 | 22 | 23 | ..\..\bin\Release 24 | ..\..\obj\Release 25 | false 26 | false 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/TagData.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public interface ITagData 8 | { 9 | bool HasTags(); 10 | 11 | bool HasTag(string tag); 12 | 13 | // if value is null, tag is "name", otherwise is "name=value" 14 | void AddTag(string name, string value = null); 15 | 16 | bool RemoveTag(string name); 17 | 18 | string GetTagValue(string name); 19 | 20 | IEnumerable<(string Key, string Value)> GetTags(); 21 | 22 | void Clear(); 23 | } 24 | 25 | public class TagData : Dictionary, ITagData 26 | { 27 | public bool HasTags() 28 | { 29 | return Count > 0; 30 | } 31 | 32 | public bool HasTag(string tag) 33 | { 34 | return ContainsKey(tag); 35 | } 36 | 37 | // if value is null, tag is "name", otherwise is "name=value" 38 | public void AddTag(string name, string value = null) 39 | { 40 | this[name] = value; 41 | } 42 | 43 | public bool RemoveTag(string name) 44 | { 45 | return Remove(name); 46 | } 47 | 48 | public string GetTagValue(string name) 49 | { 50 | return TryGetValue(name, out var value) ? value : null; 51 | } 52 | 53 | public IEnumerable<(string, string)> GetTags() 54 | { 55 | foreach (var i in this) 56 | yield return (i.Key, i.Value); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/LoopNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class LoopNode : StatefulSelectionNode 6 | { 7 | public LoopNode(int sourceLine, string context, string counterVariable, bool usesAnonymousVariable) : base(sourceLine, context, counterVariable, usesAnonymousVariable) 8 | { 9 | } 10 | 11 | public override IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 12 | { 13 | var dataContext = context.LookupOrCreateDataContext(StateVariableContext); 14 | int id = (int)dataContext.GetValueInt(StateVariableName); 15 | 16 | for (int i = 0; i < ChildrenCount; ++i) 17 | { 18 | var ccase = GetChild((id + i) % ChildrenCount); 19 | 20 | if (ccase.Condition != null && !ccase.Condition.EvaluateBool(context)) 21 | continue; 22 | 23 | ccase.Condition?.OnVisited(context); 24 | 25 | // Set next value (loop) 26 | dataContext.SetValueInt(StateVariableName, (long)((id + i + 1) % ChildrenCount)); 27 | 28 | if (ccase.ChildrenCount > 0) 29 | { 30 | return ccase.GetChild(0); 31 | } 32 | return this.FindNextSibling(); 33 | } 34 | 35 | return this.FindNextSibling(); 36 | } 37 | 38 | public override string PrintPayload() 39 | { 40 | return ">> " + StateVariableContext + StateVariableName; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /docs/Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.0.19b] - 2024-07-15 4 | 5 | ### Added 6 | - Added Options Alternatives (Check docs for syntax) 7 | 8 | ### Changed 9 | - Refactored Choices interface. Now, GetOption(i) will return an IOptions, with a slightly different and simplified interface. 10 | An option will be checked for availability and the content is read with the following methods: 11 | 12 | bool IsAvailable(IDialogueContext context); 13 | 14 | For better understanding, ICheckable is now used for content that provides Challenge Checks, while IConditional is used for content with conditions. 15 | 16 | - In the Vscode Extension, "Assign IDs to all nodes" was changed into "Assign IDs to all content", in order to include options too. 17 | 18 | ### Fixed 19 | - Fixed some syntax Highlight bugs 20 | 21 | ## [0.0.17b] - 2024-06-10 22 | 23 | ### Added 24 | - Added Multi-line Comments 25 | - Added Disable Feature 26 | 27 | ### Fixed 28 | - Fixed minor bugs 29 | 30 | ## [0.0.14b] - 2024-05-20 31 | 32 | ### Added 33 | - Added Cancel Node ( -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/IDialogueNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IDialogueNode: ICheckableContent 6 | { 7 | string Label { get; set; } 8 | string PreCheck { get; set; } 9 | 10 | IDialogueBlock Block { get; set; } 11 | int BlockId { get; set; } 12 | 13 | string PrintSubtree(string indentationPrefix, string indentationUnit); 14 | string PrintPayload(); 15 | 16 | InterruptibleNode InnermostInterruptibleNode {get; set;} 17 | 18 | string LookUpTabsPrefix(string indentationUnit); 19 | } 20 | 21 | public static class IDialogueNodeMethods 22 | { 23 | public static bool IsBranching(this IDialogueNode node) 24 | { 25 | return node.Condition != null || node.PreCheck != null; 26 | } 27 | 28 | public static IDialogueNode FindNextSibling(this IDialogueNode node) 29 | { 30 | do 31 | { 32 | var block = node.Block; 33 | int nextId = block.GetNextIndex(node.BlockId); 34 | 35 | if (nextId >= 0) 36 | { 37 | return block.GetChild(nextId); 38 | } 39 | 40 | switch (block.NextBlockPolicy) 41 | { 42 | case NextBlockPolicy.End: 43 | return null; 44 | case NextBlockPolicy.ReturnToParent: 45 | return block.Parent; 46 | } 47 | 48 | node = block.Parent; 49 | } while (node != null); 50 | 51 | return null; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/AssignmentStatement.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class BoolAssignmentStatement : IStatement 6 | { 7 | public string Context = ""; 8 | public string Name = ""; 9 | public IBoolValue Value; 10 | 11 | public void Execute(IDialogueContext context) 12 | { 13 | context.LookupOrCreateDataContext(Context).SetValueBool(Name, Value.EvaluateBool(context)); 14 | } 15 | 16 | public override string ToString() 17 | { 18 | return Context + Name + " = " + Value.ToString(); 19 | } 20 | } 21 | 22 | public class IntegerAssignmentStatement : IStatement 23 | { 24 | public string Context = ""; 25 | public string Name = ""; 26 | public IIntegerValue Value; 27 | 28 | public virtual void Execute(IDialogueContext context) 29 | { 30 | context.LookupOrCreateDataContext(Context).SetValueInt(Name, Value.EvaluateInteger(context)); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return Context + Name + " = " + Value.ToString(); 36 | } 37 | } 38 | 39 | public class SymbolAssignmentStatement : IStatement 40 | { 41 | public string Context = ""; 42 | public string Name = ""; 43 | public ISymbolValue Value; 44 | 45 | public void Execute(IDialogueContext context) 46 | { 47 | context.LookupOrCreateDataContext(Context).SetValueSymbol(Name, Value.EvaluateSymbol(context)); 48 | } 49 | 50 | public override string ToString() 51 | { 52 | return Context + Name + " = " + Value.ToString(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Code/OnceValue.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class OnceValue : IBoolValue, IStatefulElement 6 | { 7 | public string StateVariableName { get; set; } 8 | public string StateVariableContext { get; set; } 9 | public bool UsesAnonymousVariable { get; set; } 10 | 11 | public OnceValue(string variableName, string context, bool usesAnonymousVariable) 12 | { 13 | StateVariableName = variableName; 14 | StateVariableContext = context; 15 | UsesAnonymousVariable = usesAnonymousVariable; 16 | } 17 | 18 | public bool EvaluateBool(IDialogueContext context) 19 | { 20 | var c = context.LookupDataContext(StateVariableContext); 21 | if (c == null) 22 | return true; 23 | 24 | return !c.GetValueBool(StateVariableName); 25 | } 26 | 27 | public void OnVisited(IDialogueContext context) 28 | { 29 | context.LookupOrCreateDataContext(StateVariableContext).SetValueBool(StateVariableName, true); 30 | } 31 | 32 | public void Reset(IDialogueContext context) 33 | { 34 | context.LookupOrCreateDataContext(StateVariableContext).SetValueBool(StateVariableName, false); 35 | } 36 | 37 | public void Traverse(System.Action action) 38 | { 39 | action.Invoke(this); 40 | } 41 | 42 | public override string ToString() 43 | { 44 | if (UsesAnonymousVariable || string.IsNullOrEmpty(StateVariableName)) 45 | return "once"; 46 | return "once(" + StateVariableContext + StateVariableName + ")" ; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/ClampNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | using System; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public class ClampNode : StatefulSelectionNode 8 | { 9 | 10 | public ClampNode(int sourceLine, string context, string counterVariable, bool usesAnonymousVariable) : base(sourceLine, context, counterVariable, usesAnonymousVariable) 11 | { 12 | } 13 | 14 | public override IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context) 15 | { 16 | var dataContext = context.LookupOrCreateDataContext(StateVariableContext); 17 | 18 | int id = (int)dataContext.GetValueInt(StateVariableName); 19 | 20 | // Clamp value 21 | if (id >= ChildrenCount && ChildrenCount > 0) 22 | id = ChildrenCount - 1; 23 | 24 | for (int i = id; i < ChildrenCount; ++i) 25 | { 26 | var ccase = GetChild(i); 27 | 28 | if (ccase.Condition != null && !ccase.Condition.EvaluateBool(context)) 29 | continue; 30 | 31 | ccase.Condition?.OnVisited(context); 32 | 33 | dataContext.SetValueInt(StateVariableName, Math.Min(id + 1, ChildrenCount - 1)); 34 | 35 | if (ccase.ChildrenCount > 0) 36 | { 37 | return ccase.GetChild(0); 38 | } 39 | return this.FindNextSibling(); 40 | } 41 | 42 | dataContext.SetValueInt(StateVariableName, ChildrenCount - 1); 43 | return this.FindNextSibling(); 44 | } 45 | 46 | public override string PrintPayload() 47 | { 48 | return ">>> " + StateVariableContext + StateVariableName; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Samwise/Samwise.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "build", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "build", 22 | "${workspaceFolder}/SamwiseTest/SamwiseTest.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "publish", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "publish", 34 | "${workspaceFolder}/SamwiseTest/SamwiseTest.csproj", 35 | "/property:GenerateFullPaths=true", 36 | "/consoleloggerparameters:NoSummary" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | }, 40 | { 41 | "label": "watch", 42 | "command": "dotnet", 43 | "type": "process", 44 | "args": [ 45 | "watch", 46 | "run", 47 | "${workspaceFolder}/SamwiseTest/SamwiseTest.csproj", 48 | "/property:GenerateFullPaths=true", 49 | "/consoleloggerparameters:NoSummary" 50 | ], 51 | "problemMatcher": "$msCompile" 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/SequenceBlock.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public class SequenceBlock : IDialogueBlock 8 | { 9 | public virtual NextBlockPolicy NextBlockPolicy => NextBlockPolicy.ParentNext; 10 | public IBlockContainerNode Parent { get; private set; } 11 | public int ChildrenCount => children.Count; 12 | public IDialogueNode GetChild(int i) => children[i]; 13 | 14 | public SequenceBlock(IBlockContainerNode parent) { Parent = parent; } 15 | 16 | public void PushChild(IDialogueNode node) 17 | { 18 | node.Block = this; 19 | node.BlockId = children.Count; 20 | 21 | children.Add(node); 22 | } 23 | 24 | public void PopChild() 25 | { 26 | children[children.Count - 1].Block = null; 27 | children.RemoveAt(children.Count - 1); 28 | } 29 | 30 | public int GetNextIndex(int i) 31 | { 32 | ++i; 33 | 34 | if (i < ChildrenCount) 35 | return i; 36 | return -1; 37 | } 38 | 39 | public virtual string PrintSubtree(string indentationPrefix, string indentationUnit) 40 | { 41 | string o = ""; 42 | for (int i=0; i children = new List(); 55 | } 56 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/Machine/IDialogueContext.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public interface IDialogueContext 6 | { 7 | DialogueStatus Status { get; } 8 | long Uid { get; } 9 | 10 | IDialogueNode Current { get; } 11 | IDialogueContext Parent { get; } 12 | 13 | bool IsEnded { get; } 14 | 15 | bool Advance(); 16 | bool Choose(IOption choice); 17 | void CompleteChallenge(bool passed); 18 | void Stop(); 19 | bool TryResolveMissingDialogues(); 20 | 21 | IDataRoot DataRoot { get; } 22 | IDataContext DataContext { get; } 23 | object ExternalContext { get; } 24 | } 25 | 26 | public static class IDialogueContextMethods 27 | { 28 | public static IDialogueContext Root(this IDialogueContext context) 29 | { 30 | var c = context; 31 | 32 | while (c.Parent != null) 33 | c = c.Parent; 34 | 35 | return c; 36 | } 37 | public static IDataContext LookupDataContext(this IDialogueContext context, string contextName) 38 | { 39 | if (string.IsNullOrEmpty(contextName)) 40 | return context.DataContext; // local 41 | return context.DataRoot.LookupDataContext(contextName); // global 42 | } 43 | 44 | public static IDataContext LookupOrCreateDataContext(this IDialogueContext context, string contextName) 45 | { 46 | if (string.IsNullOrEmpty(contextName)) 47 | return context.DataContext; // local 48 | return context.DataRoot.LookupOrCreateDataContext(contextName); // global 49 | } 50 | 51 | public static int GetRandom(this IDialogueContext context) 52 | { 53 | return context.DataRoot.GetRandom(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /docs/Vscode_Write.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code extension 2 | 3 | !> This page is under development 4 | 5 | 6 | ## Multi-line text 7 | Samwise enables you to create multi-line text, which is useful when a single line of speech is excessively long. To ensure your line continues without breaking, you can use this special character: ↵ 8 | 9 | To produce this character, press *Shift+Enter*. 10 | 11 | ## Toolbox 12 | 13 | 1. In the Toolbox panel, you can find shortcuts to create basic dialogue nodes quickly. As you'll notice, there's a fair number of nodes available in Samwise. Each one offers you a different tool for building your interactive narrative. 14 | 2. Click on the corresponding button to paste a node into your text. 15 | 16 | ## Preview the Dialogue in-editor 17 | 18 | When a dialogue is selected in the text editor, the 'Player' panel of the Samwise extension allows you to launch that dialogue. You have the option to start the dialogue from the beginning or directly from the selected point. 19 | 20 | To do this, click on the "Run" button or the "Run from Node" button. 21 | 22 | You can also launch the Run commands via the context menu by right-clicking on a line within the text editor. 23 | 24 | In the same panel, you will see chat balloons appear, prompting user input. These balloons serve as a preview of the dialogue in execution. By clicking on "Advance," as prompted, you can progress the flow. When you encounter a choice node, instead of the simple "Advance" button, there will be a set of buttons representing the available options. 25 | 26 | Additionally, special dialogues, such as the challenge check dialogue, may appear during the dialogue preview. This type of dialogue allows you to simulate success or failure. 27 | 28 | ## Other Editor Features 29 | 30 | ### Error Diagnostics 31 | ### Auto-completion 32 | ### Go to Label 33 | 34 | ### Show/Hide Tags 35 | 36 | ### Move Samwise Panel from Primary Activity Bar 37 | 38 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Symbol.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public enum Symbol 6 | { 7 | Invalid, 8 | Fallback, // ? 9 | Loop, // >> 10 | Clamp, // >>> 11 | PingPong, // >< 12 | Score, // % 13 | Choice, // : 14 | Says, // > 15 | Fork, // => 16 | Join, // <= 17 | Await, // <=> 18 | Cancel, // >"; 38 | case Symbol.Clamp: 39 | return ">>>"; 40 | case Symbol.PingPong: 41 | return "><"; 42 | case Symbol.Choice: 43 | return ":"; 44 | case Symbol.Says: 45 | return ">"; 46 | case Symbol.Fork: 47 | return "=>"; 48 | case Symbol.Join: 49 | return "<="; 50 | case Symbol.Cancel: 51 | return ""; 54 | case Symbol.Check: 55 | return "$"; 56 | case Symbol.Interrupt: 57 | return "!"; 58 | case Symbol.ResetAndInterrupt: 59 | return "!!"; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Samwise License v1.0 2 | 3 | Copyright (c) 2022-2024, Davide 'PeevishDave' Barbieri. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted to any person obtaining a copy of this 7 | software and associated documentation files (the "Software") provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. If the Software is used in any product that is distributed in binary 17 | form and that displays the list of its contributors (for example, the 18 | credits screen of a video game), such list must also display either the 19 | line "written in Samwise™" and/or an acknowledgement which includes both the 20 | name of the Software ("Samwise™ Dialogue System") and the name of the copyright 21 | holder ("Davide 'PeevishDave' Barbieri"). For such products condition 2 22 | is to be considered optional. 23 | 24 | 4. The Software may not be used to create or develop a derivative work that serves 25 | as a direct substitute or alternative to the Software, or any similar dialogue system, 26 | intended for distribution or use by third parties. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 31 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 33 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 34 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/SelectionNode.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public abstract class SelectionNode : DialogueNode, IAutoNode, IBlockContainerNode, IMultiCaseNode 8 | { 9 | public int ChildrenCount => children.Count; 10 | public SelectionCase GetChild(int i) => children[i]; 11 | IDialogueBlock IBlockContainerNode.GetChild(int i) => children[i]; 12 | public int CasesCount => children.Count; 13 | public ICase GetCase(int i) => children[i]; 14 | 15 | public SelectionNode(int sourceLine) : base(sourceLine, sourceLine) 16 | { 17 | } 18 | 19 | public void AddCase(SelectionCase item) 20 | { 21 | children.Add(item); 22 | } 23 | 24 | public abstract IDialogueNode Next(IDialogueSet dialogues, IDialogueContext context); 25 | 26 | public override string PrintSubtree(string indentationPrefix, string indentationUnit) 27 | { 28 | string o = GetPreambleString(indentationPrefix) + PrintPayload() + GetTagsString() + "\n"; 29 | 30 | for (int i = 0; i < children.Count; ++i) 31 | o += children[i].PrintSubtree(indentationUnit + indentationPrefix, indentationUnit) + (i == children.Count - 1 ? "" : "\n"); 32 | 33 | return o; 34 | } 35 | 36 | public string LookUpChildBlockTabsPrefix(IDialogueBlock childBlock, string indentationUnit) 37 | { 38 | string prefix = indentationUnit + indentationUnit; // tab + "-" + tab 39 | 40 | var parentPrefix = Block?.LookUpChildNodeTabsPrefix(this, indentationUnit) ?? null; 41 | 42 | if (parentPrefix != null) 43 | prefix = parentPrefix + prefix; 44 | 45 | return prefix; 46 | } 47 | 48 | List children = new List(); 49 | } 50 | } -------------------------------------------------------------------------------- /vscode/src/samwiseHover.ts: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | import * as vscode from 'vscode'; 4 | 5 | import * as samwiseVM from './samwiseVM'; 6 | import { StatsInfo } from './common'; 7 | import { titleRegex } from './common'; 8 | 9 | var path = require('path'); 10 | 11 | export class SamwiseHoverProvider implements vscode.HoverProvider { 12 | provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { 13 | 14 | const value = samwiseVM.cachedSymbols.get(document.fileName); 15 | 16 | if (value) { 17 | for (let dialogueInfo of value) { 18 | if (position.line >= dialogueInfo.startLine - 1 && position.line < dialogueInfo.endLine) { 19 | 20 | // Check title regex 21 | if (!titleRegex.test(document.lineAt(position.line).text)) { 22 | continue; 23 | } 24 | 25 | const stats: (StatsInfo) = JSON.parse(samwiseVM.getDialogueStatistics(dialogueInfo.symbol)); 26 | 27 | let title = stats.title; 28 | if (title.trim().length === 0) { 29 | title = "Untitled"; 30 | } 31 | 32 | const content = new vscode.MarkdownString(`

${title}

`); 33 | content.appendMarkdown(`Label: ${dialogueInfo.symbol}
`); 34 | content.appendMarkdown(`Words: ${stats.words}
`); 35 | content.appendMarkdown(`Nodes: ${stats.nodes}`); 36 | content.supportHtml = true; 37 | content.isTrusted = true; 38 | 39 | return { 40 | contents: [content] 41 | }; 42 | } 43 | } 44 | } 45 | 46 | return null; 47 | } 48 | } -------------------------------------------------------------------------------- /src/Samwise.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31313.79 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samwise", "Samwise\Samwise.csproj", "{BE0C52A1-182E-4FE2-ABC5-C724A589613E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamwiseTest", "SamwiseTest\SamwiseTest.csproj", "{E0D16269-61A9-4569-984C-5BBB4513B83F}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BE0C52A1-182E-4FE2-ABC5-C724A589613E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {BE0C52A1-182E-4FE2-ABC5-C724A589613E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {BE0C52A1-182E-4FE2-ABC5-C724A589613E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {BE0C52A1-182E-4FE2-ABC5-C724A589613E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E0D16269-61A9-4569-984C-5BBB4513B83F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E0D16269-61A9-4569-984C-5BBB4513B83F}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E0D16269-61A9-4569-984C-5BBB4513B83F}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E0D16269-61A9-4569-984C-5BBB4513B83F}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {D172A2DB-9495-4FB0-B3D3-8A029DDE4F68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {D172A2DB-9495-4FB0-B3D3-8A029DDE4F68}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {D172A2DB-9495-4FB0-B3D3-8A029DDE4F68}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {D172A2DB-9495-4FB0-B3D3-8A029DDE4F68}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {AA881475-8357-4437-B939-03ED2C8798D4} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /vscode/src/samwiseSymbols.ts: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | import * as vscode from 'vscode'; 4 | 5 | import type { SymbolInfo } from "./common"; 6 | 7 | import * as parser from './samwiseVM'; 8 | 9 | export class SamwiseDocumentSymbolProvider implements vscode.DocumentSymbolProvider { 10 | async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { 11 | // wait until initalized 12 | await parser.waitUntilInitialized(); 13 | 14 | // Parse 15 | let info: any = parser.parseText(document.fileName, document.getText()); 16 | 17 | if (info === undefined || info.errors) { 18 | return this._previousInfo; 19 | } 20 | else { 21 | const newSymbols = createSymbols(info); 22 | this._previousInfo = newSymbols; 23 | return newSymbols; 24 | } 25 | } 26 | 27 | _previousInfo: vscode.DocumentSymbol[] = []; 28 | } 29 | 30 | function createSymbols(elements: SymbolInfo[]): vscode.DocumentSymbol[] { 31 | let results: vscode.DocumentSymbol[] = []; 32 | 33 | elements.forEach(element => { 34 | 35 | let symbol = createSymbolForElement(element); 36 | if (element.children) { 37 | symbol.children = createSymbols(element.children); 38 | } 39 | 40 | results.push(symbol); 41 | }); 42 | 43 | return results; 44 | } 45 | 46 | function createSymbolForElement(info: SymbolInfo): vscode.DocumentSymbol { 47 | const fullRange = new vscode.Range(info.startLine - 1, 0, info.endLine - 1, 1); 48 | const nameRange = new vscode.Range(info.startLine - 1, 0, info.startLine - 1, 1); 49 | 50 | return new vscode.DocumentSymbol(info.symbol, info.name, toSymbolKind(info.type), fullRange, nameRange); 51 | } 52 | 53 | 54 | function toSymbolKind(kind: string): vscode.SymbolKind { 55 | if (kind === "Label") { 56 | return vscode.SymbolKind.Constant; 57 | } 58 | else { 59 | return vscode.SymbolKind.Module; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/DummyCodeParser.cs: -------------------------------------------------------------------------------- 1 | namespace Peevo.Samwise 2 | { 3 | public class DummyCodeParser: IExternalCodeParser 4 | { 5 | public bool Parse(string code, out IStatement statement, System.Action logError) 6 | { 7 | statement = new DummyStatement(code); 8 | return true; 9 | } 10 | 11 | public bool ParseCondition(string code, out IBoolValue expression, System.Action logError) 12 | { 13 | expression = new DummyBoolValue(code); 14 | return true; 15 | } 16 | 17 | public bool ParseAsync(string code, out IAsyncCode asyncCode, System.Action logError) 18 | { 19 | asyncCode = new DummyAsyncCode(code); 20 | return true; 21 | } 22 | } 23 | 24 | public class DummyStatement : IStatement 25 | { 26 | public string Code { get; private set; } 27 | public DummyStatement(string code) { Code = code; } 28 | 29 | public void Execute(IDialogueContext context) 30 | { 31 | 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return Code; 37 | } 38 | } 39 | 40 | public class DummyBoolValue : IBoolValue 41 | { 42 | public string Code { get; private set; } 43 | public DummyBoolValue(string code) { Code = code; } 44 | 45 | public bool EvaluateBool(IDialogueContext context) 46 | { 47 | return true; 48 | } 49 | 50 | public override string ToString() 51 | { 52 | return Code; 53 | } 54 | 55 | public void OnVisited(IDialogueContext context) {} 56 | public void Traverse(System.Action action) { action.Invoke(this); } 57 | } 58 | 59 | public class DummyAsyncCode : IAsyncCode 60 | { 61 | public string Code { get; private set; } 62 | public DummyAsyncCode(string code) { Code = code; } 63 | 64 | public override string ToString() 65 | { 66 | return Code; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /vscode/src/samwiseDefinitions.ts: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | import * as vscode from 'vscode'; 4 | 5 | import * as samwiseVM from './samwiseVM'; 6 | import { SymbolInfo } from './common'; 7 | 8 | var path = require('path'); 9 | 10 | export class SamwiseDefinitionProvider implements vscode.DefinitionProvider { 11 | provideDefinition( 12 | document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.Location | undefined { 13 | return this.findDefinition(document, position); 14 | } 15 | 16 | public findDefinition( 17 | document: vscode.TextDocument, position: vscode.Position): vscode.Location | undefined { 18 | const line = position.line + 1; 19 | const symbol: string = samwiseVM.getDestinationSymbol(document.fileName, line); 20 | 21 | let location: vscode.Location | undefined = undefined; 22 | 23 | if (symbol === undefined || symbol === "") { 24 | return; 25 | } 26 | 27 | const dialogueSymbol = samwiseVM.getDialogueSymbolFromLine(document.fileName, line); 28 | 29 | // search symbol 30 | samwiseVM.cachedSymbols.forEach((value: SymbolInfo[], file: string) => { 31 | value.forEach((dialogueInfo: SymbolInfo) => { 32 | if (dialogueInfo.symbol === symbol) { 33 | const uri = vscode.Uri.file(file); 34 | location = new vscode.Location(uri, new vscode.Position(dialogueInfo.startLine - 1, 0)); 35 | return; 36 | } 37 | 38 | const isLocal = dialogueInfo.symbol === dialogueSymbol; 39 | 40 | dialogueInfo.children.forEach((labelInfo: SymbolInfo) => { 41 | let targetSymbol: string = isLocal ? labelInfo.symbol : dialogueInfo.symbol + "." + labelInfo.symbol; 42 | 43 | if (targetSymbol === symbol) { 44 | location = new vscode.Location(vscode.Uri.file(file), new vscode.Position(labelInfo.startLine - 1, 0)); 45 | return; 46 | } 47 | }); 48 | }); 49 | }); 50 | 51 | return location; 52 | } 53 | } -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | **Samwise™** is a game development tool for writing complex interactive dialogues. 4 | 5 | The objective of this tool is to empower writers and designers to craft intricate, branching narratives, providing comprehensive control over dialogue scripting. This allows for the creation of naturally flowing interactions that are essential for immersive gaming experiences. 6 | 7 | **Samwise™** consists of three components: 8 | 1. A scripting language for writing interactive dialogues; 9 | 2. A runtime library for parsing such files and supporting the execution of dialogues in a videogame (or any other interactive software); 10 | 3. A Visual Studio Code extension that assists the developer in the task of writing dialogues and testing them quickly. 11 | 12 | ![image info](./images/screenshot.jpg) 13 | 14 | ## Licensing 15 | 16 | **Samwise™** is free to be used in commercial and non-commercial products, but it requires you to attribute the work in the manner specified in the [License](LICENSE.md). For example, in order to be used in a video game it's enough to include the ["written in Samwise" logo](logo/samwise-both-large.png) and/or the acknowledgment "Samwise™ Dialogue System by Davide 'PeevishDave' Barbieri" in the credits screen. 17 | 18 | **Samwise™**'s license covers the description of the language and the runtime library, but it does not extend to the dialogues you write in that language. Your narrative is, of course, entirely your property. 19 | 20 | ## How can you help me 21 | 22 | Please consider to support my work on [Github Sponsors](https://github.com/sponsors/davidebarbieri)! 23 | 24 | I also appreciate all feedback, bug reports, and user suggestions regarding the use of Samwise. Your insights are invaluable in improving its functionality and user experience. 25 | 26 | !> Please note that while I am delighted to see Samwise being utilized in video game projects made by other developers, it remains a personal project mainly driven by fun, and as such, **I do not accept pull requests**. If you have suggestions for improvement, I kindly ask that you specify what aspect you'd like to see enhanced rather than providing direct code contributions. This approach ensures that any modifications align with the original vision and maintain the enjoyment of the project for its creator. -------------------------------------------------------------------------------- /src/SamwiseWasm/DebugInformation.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public class DebugInformation 8 | { 9 | public string SourceFile {get; private set;} 10 | 11 | public DebugInformation(string sourceFile = "") 12 | { 13 | SourceFile = sourceFile; 14 | } 15 | 16 | // Gather information from dialogue. In order to work, dialogue must be in the same source file (real or virtual) as any other dialogue that's gathered in this object. 17 | public void Gather(Dialogue dialogue) 18 | { 19 | for (int i=dialogue.SourceLineStart; i<=dialogue.SourceLineEnd; ++i) 20 | { 21 | lineToDialogue[i] = dialogue; 22 | } 23 | 24 | GatherBlock(dialogue); 25 | } 26 | 27 | public Dialogue GetDialogue(int sourceLine) 28 | { 29 | if (sourceLine < 0) 30 | return null; 31 | 32 | return lineToDialogue.TryGetValue(sourceLine, out var dialogue) ? dialogue : null; 33 | } 34 | 35 | public IDialogueNode GetDialogueNode(int sourceLine) 36 | { 37 | if (sourceLine < 0) 38 | return null; 39 | 40 | return lineToNode.TryGetValue(sourceLine, out var node) ? node : null; 41 | } 42 | 43 | void GatherBlock(IDialogueBlock block) 44 | { 45 | for (int i=0; i < block.ChildrenCount; ++i) 46 | { 47 | var node = block.GetChild(i); 48 | 49 | for (int line=node.SourceLineStart; line<=node.SourceLineEnd; ++line) 50 | lineToNode[line] = node; 51 | 52 | if (node is IBlockContainerNode blockNode) 53 | { 54 | for (int j=0; j lineToDialogue = new Dictionary(); 63 | Dictionary lineToNode = new Dictionary(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/SelectionCase.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2022 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | public class SelectionCase : SequenceBlock, ICase 6 | { 7 | public IBoolValue Condition { get; set; } 8 | public ITagData TagData { get; set; } 9 | 10 | public int SourceLine {get; internal set;} 11 | public int SourceLineStart => SourceLine; 12 | public int SourceLineEnd => SourceLine; 13 | 14 | public new SelectionNode Parent => (SelectionNode)base.Parent; 15 | IMultiCaseNode ICase.Parent => (SelectionNode)base.Parent; 16 | public Dialogue GetDialogue() => Parent.GetDialogue(); 17 | 18 | public SelectionCase(int sourceLine, SelectionNode parent, IBoolValue condition, TagData tagData) : base(parent) 19 | { 20 | Condition = condition; 21 | TagData = tagData; 22 | SourceLine = sourceLine; 23 | } 24 | 25 | public override string PrintSubtree(string indentationPrefix, string indentationUnit) 26 | { 27 | var s = indentationPrefix + PrintPayload() + DialogueNode.GetTagsString(TagData); 28 | 29 | if (base.ChildrenCount > 0) 30 | s += "\n" + base.PrintSubtree(indentationUnit + indentationPrefix, indentationUnit); 31 | 32 | return s; 33 | } 34 | 35 | public string PrintPayload() 36 | { 37 | var s = "-"; 38 | 39 | var attributes = GetAttributesString(); 40 | 41 | if (!string.IsNullOrEmpty(attributes)) 42 | s += " [" + GetAttributesString() + "]"; 43 | 44 | return s; 45 | } 46 | 47 | public string PrintLine(string indentationUnit) 48 | { 49 | return Parent.LookUpTabsPrefix(indentationUnit) + indentationUnit + PrintPayload() + DialogueNode.GetTagsString(TagData); 50 | } 51 | 52 | public override string ToString() 53 | { 54 | return PrintLine("\t"); 55 | } 56 | 57 | public string GenerateUidPreamble(Dialogue dialogue) 58 | { 59 | return dialogue.Label + "_"; 60 | } 61 | 62 | string GetAttributesString() 63 | { 64 | string attributes = ""; 65 | 66 | if (Condition != null) 67 | attributes = Condition.ToString(); 68 | 69 | return attributes; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Samwise/Runtime/LocalDataContext.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | namespace Peevo.Samwise 4 | { 5 | internal class LocalDataContext : DataContext 6 | { 7 | public event System.Action onLocalBoolDataChanged; 8 | public event System.Action onLocalIntDataChanged; 9 | public event System.Action onLocalSymbolDataChanged; 10 | public event System.Action onLocalDataClear; 11 | public event System.Action onLocalClear; 12 | 13 | internal IDialogueContext DialogueContext; 14 | 15 | internal LocalDataContext() 16 | { 17 | onBoolDataChanged += OnBoolDataChanged; 18 | onIntDataChanged += OnIntDataChanged; 19 | onSymbolDataChanged += OnSymbolDataChanged; 20 | onDataClear += OnDataClear; 21 | onClear += OnClear; 22 | } 23 | 24 | void OnClear() 25 | { 26 | // Fire only if the dialogue is running 27 | //if (!DialogueContext.IsEnded) 28 | onLocalClear?.Invoke(DialogueContext); 29 | } 30 | 31 | private void OnSymbolDataChanged(string name, string prevValue, string newValue) 32 | { 33 | // Fire only if the dialogue is running 34 | if (!DialogueContext.IsEnded) 35 | onLocalSymbolDataChanged?.Invoke(DialogueContext, name, prevValue, newValue); 36 | } 37 | 38 | private void OnIntDataChanged(string name, long prevValue, long newValue) 39 | { 40 | // Fire only if the dialogue is running 41 | if (!DialogueContext.IsEnded) 42 | onLocalIntDataChanged?.Invoke(DialogueContext, name, prevValue, newValue); 43 | } 44 | 45 | private void OnBoolDataChanged(string name, bool prevValue, bool newValue) 46 | { 47 | // Fire only if the dialogue is running 48 | if (!DialogueContext.IsEnded) 49 | onLocalBoolDataChanged?.Invoke(DialogueContext, name, prevValue, newValue); 50 | } 51 | 52 | private void OnDataClear(string name) 53 | { 54 | // Fire only if the dialogue is running 55 | if (!DialogueContext.IsEnded) 56 | onLocalDataClear?.Invoke(DialogueContext, name); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /samples/test.sam: -------------------------------------------------------------------------------- 1 | § Test Dialogue 1 2 | 3 | * This is a caption. A really good-looking caption. 4 | 5 | peter> I'm telling something. 6 | mark> Me too, but my dialogue line is tagged # tag1, tag2 7 | (retry) mark> Do you want me to explain what tags are? 8 | [once] peter: 9 | - Of course... what are tags used for? 10 | mark> Well, it's meta-data. The developers could use them for anything they need. 11 | peter> For example? 12 | mark> Well... to associate informations to the dialogue line #unskippable 13 | mark> Like the character animation that must be played along with the line. 14 | peter> Mmmh... am I smiling? #smile 15 | mark> Oooh! Yeah, you nailed it! 16 | - Ok, sure. Tags. Boring stuff. 17 | mark> Come on, try to be more cooperative. 18 | -> retry 19 | -- [5.0s] Hit Mark 20 | mark> Have you gone mad? 21 | peter> Sorry, I felt the urge to do it. Like now or never. 22 | - [iVariable1 > 5] Please, stop. 23 | mark> You take the joy out of every tutorial, Peter. 24 | mark> Okay, where were we? 25 | ? 26 | - [bVariable2] 27 | mark> I'll tell this if bVariable2 is true. 28 | peter> And I'll reply you with this. 29 | - [bVariable3] 30 | mark> I'll tell this if bVariable3 is true. 31 | peter> And I'll reply you with this. 32 | - 33 | mark> Otherwise I'll tell this. 34 | peter> Thank you for telling me. 35 | [bExtraPoliteness] peter> That's pretty interesting, my dear. 36 | mark> I'm glad you appreciate 37 | % 38 | - [5x] 39 | mark> This is a random branch. There's a high probability I'm saying this. 40 | peter> How much probable? 41 | mark> Five times than a branch with the 1x attribute (or no attribute). 42 | - 43 | mark> There is also probability that I will say this! 44 | peter> But much less likely! 45 | 46 | [iVariable1 > 0] mark> ok, listen to me... 47 | mark> ...this is a shortcut node, I'll say this only if the previous condition is met. 48 | peter> Oh, it's a more convenient way to write branches than the "?" node 49 | [iVariable1 > 0] mark> And it can be nested too of course! 50 | peter> That's so cool. 51 | parallelDialogue => TestDialogue2 52 | mark> I'm telling this, then I'll wait for Test Dialogue 1 to be completed, before continuing 53 | parallelDialogue <= 54 | mark> Yay! We're done! 55 | 56 | ┌────────────────────┐ 57 | │ Test Dialogue 2 │▒ 58 | └────────────────────┘▒ 59 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 60 | 61 | mark> Is anyone there? 62 | loise> Me! 63 | mark> Anybody else? 64 | -------------------------------------------------------------------------------- /src/Samwise/Runtime/Nodes/OptionGroup.cs: -------------------------------------------------------------------------------- 1 | // (c) Copyright 2024 Davide 'PeevishDave' Barbieri 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Peevo.Samwise 6 | { 7 | public class OptionGroup : SequenceBlock 8 | { 9 | // Note to me: return property cannot be overridden because it's a property of the block, not of the option; 10 | // Imagine you have a jump into an option block that has two alternatives (a simple one, and a "return" alternative): 11 | // when the block is over what should happen? 12 | 13 | public int GroupId { get; set; } // Used just to recover state when node uid were not saved 14 | 15 | public bool ReturnOption { get; set; } 16 | public bool MuteOption { get; set; } 17 | 18 | public override NextBlockPolicy NextBlockPolicy => ReturnOption ? NextBlockPolicy.ReturnToParent : NextBlockPolicy.ParentNext; 19 | 20 | public int OptionsCount => options.Count; 21 | public Option GetOption(int index) => options[index]; 22 | 23 | public OptionGroup(ChoiceNode parent, int groupId, bool muteOption, bool returnOption) : base(parent) 24 | { 25 | GroupId = groupId; 26 | MuteOption = muteOption; 27 | ReturnOption = returnOption; 28 | } 29 | 30 | public void AddOption(Option option) 31 | { 32 | options.Add(option); 33 | } 34 | 35 | public Option GetAvailableOption(IDialogueContext context) 36 | { 37 | for (int i=0, count=options.Count; i 0) 61 | s += "\n" + base.PrintSubtree(indentationUnit + indentationPrefix, indentationUnit); 62 | 63 | return s; 64 | } 65 | 66 | public bool IsFirstOption(Option option) 67 | { 68 | return options.Count > 0 && options[0] == option; 69 | } 70 | 71 | List