From 3755392da12abc7f4a9c6dc4c22c2d50f32aa4df Mon Sep 17 00:00:00 2001
From: Christopher <chrisgregan@gmail.com>
Date: Tue, 27 Sep 2016 16:40:54 +0100
Subject: [PATCH] Added Block signals

---
 Assets/Fungus/Scripts/Components/Block.cs     |   3 +
 Assets/Fungus/Scripts/Signals/BlockSignals.cs |  36 +++
 .../Scripts/Signals/BlockSignals.cs.meta      |  12 +
 .../Fungus/Scripts/Signals/WriterSignals.cs   |   6 +-
 Assets/Tests/Signals/BlockSignalsTester.cs    |  53 +++++
 .../Tests/Signals/BlockSignalsTester.cs.meta  |  12 +
 Assets/Tests/Signals/SignalsTests.unity       | 221 +++++++++++++++++-
 7 files changed, 337 insertions(+), 6 deletions(-)
 create mode 100644 Assets/Fungus/Scripts/Signals/BlockSignals.cs
 create mode 100644 Assets/Fungus/Scripts/Signals/BlockSignals.cs.meta
 create mode 100644 Assets/Tests/Signals/BlockSignalsTester.cs
 create mode 100644 Assets/Tests/Signals/BlockSignalsTester.cs.meta

diff --git a/Assets/Fungus/Scripts/Components/Block.cs b/Assets/Fungus/Scripts/Components/Block.cs
index 41278dfd..9be9693f 100644
--- a/Assets/Fungus/Scripts/Components/Block.cs
+++ b/Assets/Fungus/Scripts/Components/Block.cs
@@ -213,6 +213,7 @@ namespace Fungus
 
             var flowchart = GetFlowchart();
             executionState = ExecutionState.Executing;
+            BlockSignals.DoBlockStart(this);
 
             #if UNITY_EDITOR
             // Select the executing block & the first command
@@ -278,6 +279,7 @@ namespace Fungus
                 // This icon timer is managed by the FlowchartWindow class, but we also need to
                 // set it here in case a command starts and finishes execution before the next window update.
                 command.ExecutingIconTimer = Time.realtimeSinceStartup + FungusConstants.ExecutingIconFadeTime;
+                BlockSignals.DoCommandExecute(this, command, i, commandList.Count);
                 command.Execute();
 
                 // Wait until the executing command sets another command to jump to via Command.Continue()
@@ -298,6 +300,7 @@ namespace Fungus
 
             executionState = ExecutionState.Idle;
             activeCommand = null;
+            BlockSignals.DoBlockEnd(this);
 
             if (onComplete != null)
             {
diff --git a/Assets/Fungus/Scripts/Signals/BlockSignals.cs b/Assets/Fungus/Scripts/Signals/BlockSignals.cs
new file mode 100644
index 00000000..0492d850
--- /dev/null
+++ b/Assets/Fungus/Scripts/Signals/BlockSignals.cs
@@ -0,0 +1,36 @@
+using Fungus.Utils;
+
+namespace Fungus
+{
+    /// <summary>
+    /// Block event signalling system.
+    /// You can use this to be notified about various events in the Block execution process.
+    /// </summary>
+    public static class BlockSignals
+    {
+        #region Public members
+
+        /// <summary>
+        /// BlockStart signal. Sent when the Block starts execution.
+        /// </summary>
+        public static event BlockStartHandler OnBlockStart;
+        public delegate void BlockStartHandler(Block block);
+        public static void DoBlockStart(Block block) { if (OnBlockStart != null) OnBlockStart(block); }
+
+        /// <summary>
+        /// BlockEnd signal. Sent when the Block ends execution.
+        /// </summary>
+        public static event BlockEndHandler OnBlockEnd;
+        public delegate void BlockEndHandler(Block block);
+        public static void DoBlockEnd(Block block) { if (OnBlockEnd != null) OnBlockEnd(block); }
+
+        /// <summary>
+        /// CommandExecute signal. Sent just before a Command in a Block executes.
+        /// </summary>
+        public static event CommandExecuteHandler OnCommandExecute;
+        public delegate void CommandExecuteHandler(Block block, Command command, int commandIndex, int maxCommandIndex);
+        public static void DoCommandExecute(Block block, Command command, int commandIndex, int maxCommandIndex) { if (OnCommandExecute != null) OnCommandExecute(block, command, commandIndex, maxCommandIndex); }
+
+        #endregion
+    }
+}
diff --git a/Assets/Fungus/Scripts/Signals/BlockSignals.cs.meta b/Assets/Fungus/Scripts/Signals/BlockSignals.cs.meta
new file mode 100644
index 00000000..d3c70701
--- /dev/null
+++ b/Assets/Fungus/Scripts/Signals/BlockSignals.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 12109e10b0f034bbab8a7a5d700bc070
+timeCreated: 1474988491
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Fungus/Scripts/Signals/WriterSignals.cs b/Assets/Fungus/Scripts/Signals/WriterSignals.cs
index 01b1a67d..ff9680d5 100644
--- a/Assets/Fungus/Scripts/Signals/WriterSignals.cs
+++ b/Assets/Fungus/Scripts/Signals/WriterSignals.cs
@@ -15,22 +15,22 @@ namespace Fungus
         /// <summary>
         /// TextTagToken signal. Sent for each unique token when writing text.
         /// </summary>
-        public delegate void TextTagTokenHandler(Writer writer, TextTagToken token, int index, int maxIndex);
         public static event TextTagTokenHandler OnTextTagToken;
+        public delegate void TextTagTokenHandler(Writer writer, TextTagToken token, int index, int maxIndex);
         public static void DoTextTagToken(Writer writer, TextTagToken token, int index, int maxIndex) { if(OnTextTagToken != null) OnTextTagToken(writer, token, index, maxIndex); }
 
         /// <summary>
         /// WriterState signal. Sent when the writer changes state.
         /// </summary>
-        public delegate void WriterStateHandler(Writer writer, WriterState writerState);
         public static event WriterStateHandler OnWriterState;
+        public delegate void WriterStateHandler(Writer writer, WriterState writerState);
         public static void DoWriterState(Writer writer, WriterState writerState) { if (OnWriterState != null) OnWriterState(writer, writerState); }
 
         /// <summary>
         /// WriterInput signal. Sent when the writer receives player input.
         /// </summary>
-        public delegate void WriterInputHandler(Writer writer);
         public static event WriterInputHandler OnWriterInput;
+        public delegate void WriterInputHandler(Writer writer);
         public static void DoWriterInput(Writer writer) { if (OnWriterInput != null) OnWriterInput(writer); }
 
         /// <summary>
diff --git a/Assets/Tests/Signals/BlockSignalsTester.cs b/Assets/Tests/Signals/BlockSignalsTester.cs
new file mode 100644
index 00000000..754d9a26
--- /dev/null
+++ b/Assets/Tests/Signals/BlockSignalsTester.cs
@@ -0,0 +1,53 @@
+using UnityEngine;
+using System.Collections;
+using Fungus.Commands;
+
+namespace Fungus
+{
+    /// <summary>
+    /// Checks if Block signals are being sent correctly.
+    /// </summary>
+    [AddComponentMenu("")]
+    public class BlockSignalsTester : MonoBehaviour 
+    {
+        bool started = false;
+        bool commandExecuted = false;
+
+        void OnEnable() 
+        {
+            BlockSignals.OnBlockStart += OnBlockStart;
+            BlockSignals.OnBlockEnd += OnBlockEnd;
+            BlockSignals.OnCommandExecute += OnCommandExecute;
+        }
+
+        void OnDisable()
+        {
+            BlockSignals.OnBlockStart -= OnBlockStart;
+            BlockSignals.OnBlockEnd -= OnBlockEnd;
+            BlockSignals.OnCommandExecute -= OnCommandExecute;
+        }
+
+        void OnBlockStart(Block block)
+        {
+            IntegrationTest.Assert(block.BlockName == "Start");
+
+            started = true;
+        }
+
+        void OnBlockEnd(Block block)
+        {
+            IntegrationTest.Assert(started);
+            IntegrationTest.Assert(commandExecuted);
+
+            IntegrationTest.Pass();
+        }
+
+        void OnCommandExecute(Block block, Command command, int commandIndex, int maxCommandIndex)
+        {
+            IntegrationTest.Assert(commandIndex == 0);
+            IntegrationTest.Assert(maxCommandIndex == 1);
+            IntegrationTest.Assert(command.GetType() == typeof(Wait));
+            commandExecuted = true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Tests/Signals/BlockSignalsTester.cs.meta b/Assets/Tests/Signals/BlockSignalsTester.cs.meta
new file mode 100644
index 00000000..6fe2ffc5
--- /dev/null
+++ b/Assets/Tests/Signals/BlockSignalsTester.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5d396ca39a0454e2ca06d4aad7c5706c
+timeCreated: 1474988463
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Tests/Signals/SignalsTests.unity b/Assets/Tests/Signals/SignalsTests.unity
index c5fba771..c368d289 100644
--- a/Assets/Tests/Signals/SignalsTests.unity
+++ b/Assets/Tests/Signals/SignalsTests.unity
@@ -264,7 +264,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: 61dddfdc5e0e44ca298d8f46f7f5a915, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  selectedFlowchart: {fileID: 411939904}
+  selectedFlowchart: {fileID: 330763419}
 --- !u!4 &286067097
 Transform:
   m_ObjectHideFlags: 1
@@ -278,6 +278,129 @@ Transform:
   m_Children: []
   m_Father: {fileID: 0}
   m_RootOrder: 1
+--- !u!1 &330763414
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 142980, guid: 5e7fbc8d4eb714b279eeeef2262c1e1a, type: 2}
+  m_PrefabInternal: {fileID: 0}
+  serializedVersion: 4
+  m_Component:
+  - 4: {fileID: 330763415}
+  - 114: {fileID: 330763419}
+  - 114: {fileID: 330763418}
+  - 114: {fileID: 330763417}
+  - 114: {fileID: 330763416}
+  m_Layer: 0
+  m_Name: Flowchart
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &330763415
+Transform:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 467082, guid: 5e7fbc8d4eb714b279eeeef2262c1e1a, type: 2}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 330763414}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_Children: []
+  m_Father: {fileID: 467145139}
+  m_RootOrder: 0
+--- !u!114 &330763416
+MonoBehaviour:
+  m_ObjectHideFlags: 2
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 330763414}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3315ad2ebb85443909a1203d56d9344e, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  itemId: 1
+  indentLevel: 0
+  _duration:
+    floatRef: {fileID: 0}
+    floatVal: 1
+  durationOLD: 0
+--- !u!114 &330763417
+MonoBehaviour:
+  m_ObjectHideFlags: 2
+  m_PrefabParentObject: {fileID: 11462346, guid: 5e7fbc8d4eb714b279eeeef2262c1e1a,
+    type: 2}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 330763414}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: d2f6487d21a03404cb21b245f0242e79, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  parentBlock: {fileID: 330763418}
+--- !u!114 &330763418
+MonoBehaviour:
+  m_ObjectHideFlags: 2
+  m_PrefabParentObject: {fileID: 11433304, guid: 5e7fbc8d4eb714b279eeeef2262c1e1a,
+    type: 2}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 330763414}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3d3d73aef2cfc4f51abf34ac00241f60, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  nodeRect:
+    serializedVersion: 2
+    x: 67
+    y: 70
+    width: 120
+    height: 40
+  itemId: 0
+  blockName: Start
+  description: 
+  eventHandler: {fileID: 330763417}
+  commandList:
+  - {fileID: 330763416}
+--- !u!114 &330763419
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 11430050, guid: 5e7fbc8d4eb714b279eeeef2262c1e1a,
+    type: 2}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 330763414}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 7a334fe2ffb574b3583ff3b18b4792d3, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  version: 1
+  scrollPos: {x: 0, y: 0}
+  variablesScrollPos: {x: 0, y: 0}
+  variablesExpanded: 1
+  blockViewHeight: 400
+  zoom: 1
+  scrollViewRect:
+    serializedVersion: 2
+    x: -343
+    y: -340
+    width: 1114
+    height: 859
+  selectedBlock: {fileID: 330763418}
+  selectedCommands: []
+  variables: []
+  description: 
+  stepPause: 0
+  colorCommands: 1
+  hideComponents: 1
+  saveSelection: 1
+  localizationId: 
+  showLineNumbers: 0
+  hideCommands: []
+  luaEnvironment: {fileID: 0}
+  luaBindingName: flowchart
 --- !u!1 &411939899
 GameObject:
   m_ObjectHideFlags: 0
@@ -494,6 +617,58 @@ MonoBehaviour:
   commandList:
   - {fileID: 411939906}
   - {fileID: 411939905}
+--- !u!1 &467145137
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  serializedVersion: 4
+  m_Component:
+  - 4: {fileID: 467145139}
+  - 114: {fileID: 467145138}
+  m_Layer: 0
+  m_Name: BlockSignalsTest
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &467145138
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 467145137}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: b1dba0b27b0864740a8720e920aa88c0, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  timeout: 5
+  ignored: 0
+  succeedAfterAllAssertionsAreExecuted: 0
+  expectException: 0
+  expectedExceptionList: 
+  succeedWhenExceptionIsThrown: 0
+  includedPlatforms: -1
+  platformsToIgnore: []
+  dynamic: 0
+  dynamicTypeName: 
+--- !u!4 &467145139
+Transform:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 467145137}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_Children:
+  - {fileID: 330763415}
+  - {fileID: 676488089}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
 --- !u!1 &597475644
 GameObject:
   m_ObjectHideFlags: 0
@@ -581,6 +756,46 @@ Transform:
   m_Children: []
   m_Father: {fileID: 0}
   m_RootOrder: 0
+--- !u!1 &676488088
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  serializedVersion: 4
+  m_Component:
+  - 4: {fileID: 676488089}
+  - 114: {fileID: 676488090}
+  m_Layer: 0
+  m_Name: BlockSignalsTester
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &676488089
+Transform:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 676488088}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_Children: []
+  m_Father: {fileID: 467145139}
+  m_RootOrder: 1
+--- !u!114 &676488090
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 0}
+  m_GameObject: {fileID: 676488088}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5d396ca39a0454e2ca06d4aad7c5706c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1 &759754969
 GameObject:
   m_ObjectHideFlags: 0
@@ -642,7 +857,7 @@ Transform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 4
+  m_RootOrder: 5
 --- !u!1 &1201908855
 GameObject:
   m_ObjectHideFlags: 0
@@ -816,4 +1031,4 @@ Transform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 3
+  m_RootOrder: 4