Ejemplo n.º 1
0
 private static void enqueueDestroyActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction,
                                           CM.Animation effectingAnimation, FireBoltActionList aaq,
                                           string parentActionId, bool implicitActorInstantiation)
 {
     foreach (CM.DestroyAction da in domainAction.DestroyActions)
     {
         float  startTick = 0;
         string actorName = null;
         foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
         {
             if (domainActionParameter.Name == da.ActorNameParamName)
             {
                 if (!getActionParameterValue(storyAction, domainActionParameter, out actorName) ||
                     (implicitActorInstantiation && !actorWillBeInstantiated(actorName)))
                 {
                     break;
                 }
             }
         }
         startTick = getStartTick(storyAction, da, effectingAnimation);
         if (Destroy.ValidForConstruction(actorName))
         {
             aaq.Add(new Destroy(startTick, actorName)
             {
                 ParentActionId = parentActionId
             });
         }
     }
 }
Ejemplo n.º 2
0
 private static void enqueueAttachActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction, CM.Animation effectingAnimation,
                                          FireBoltActionList aaq, string parentActionId, bool implicitActorInstantiation)
 {
     foreach (CM.AttachAction aa in domainAction.AttachActions)
     {
         float  startTick  = 0;
         string actorName  = null;
         string parentName = null;
         bool   attach     = false;
         foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
         {
             if (domainActionParameter.Name == aa.ActorNameParamName)
             {
                 getActionParameterValue(storyAction, domainActionParameter, out actorName);
                 //TODO fail gracefully if we don't find actor param value
             }
             else if (domainActionParameter.Name == aa.ParentParamName)
             {
                 getActionParameterValue(storyAction, domainActionParameter, out parentName);
                 //TODO fail gracefully if we don't find parent param value
             }
         }
         attach    = aa.Attach;
         startTick = getStartTick(storyAction, aa, effectingAnimation);
         if ((!implicitActorInstantiation || actorWillBeInstantiated(actorName)) &&
             Create.ValidForConstruction(actorName, parentName))
         {
             aaq.Add(new Attach(startTick, actorName, parentName, attach)
             {
                 ParentActionId = parentActionId
             });
         }
     }
 }
Ejemplo n.º 3
0
        //private static void buildInitialState(FireBoltActionList aaq) //TODO actor model defaulting a la create actions
        //{
        //    var interval = new UintT(new UintV(0), new UintV(1));
        //    var initialPositions = from sentence in story.Sentences
        //                           where sentence is Predicate
        //                           let p = (Predicate)sentence
        //                           where p.Temporal &&
        //                                 p.Name == "at" &&
        //                                 p.Time is UintT &&
        //                                 p.Terms[0] is IConstant &&
        //                                 p.Terms[1] is IConstant<Coordinate2D> &&
        //                                 story.IntervalSet.IncludesOrMeetsStartOf<UintV, UintT>((UintT)p.Time, interval)
        //                           select new { Actor = p.Terms[0].Name, Location = (p.Terms[1] as IConstant<Coordinate2D>).Value };

        //    Extensions.Log("building init state creation actions");
        //    foreach (var initPos in initialPositions)
        //    {
        //        Extensions.Log(initPos.Actor + ", " + initPos.Location.ToString());
        //        CM.Actor actor;
        //        if (!cm.TryGetActor(initPos.Actor,out actor))
        //        {
        //            Extensions.Log("actor [" + initPos.Actor + "] not found in cinematic model.");
        //            continue;
        //        }

        //        string modelFileName = actor.Model;
        //        if (string.IsNullOrEmpty(modelFileName))
        //        {
        //            Extensions.Log("model name for actor[" + initPos.Actor + "] not found in cinematic model.");
        //            continue;
        //        }
        //        aaq.Add(new Create(0, initPos.Actor, modelFileName, initPos.Location.ToVector3()));
        //    }
        //}

        private static void createActors(FireBoltActionList aaq)
        {
            var actorNames = (story.ObjectSets[Impulse.v_1_336.Xml.Story.ObjectsSetName] as IFiniteObjectSet).Items.Select(c => c.Name).ToList();

            Extensions.Log("building object set based creation actions");
            foreach (var actorName in actorNames)
            {
                if (!actorActuallyDoesStuff(actorName))
                {
                    //Extensions.Log("not instantiating actor[{0}] as he does nothing in this impulse", actorName);
                    continue;
                }
                CinematicModelMetaData metaData = new CinematicModelMetaData();

                if (!getActorMetaData(actorName, out metaData))
                {
                    Extensions.Log("cannot auto-create actor[{0}]", actorName);
                    continue;
                }
                Extensions.Log("building object set based create for actor[{0}]", actorName);
                Create create = new Create(0, actorName, metaData.ModelName, new Vector3(-10000, 0, -10000), metaData, null, true);
                aaq.Add(create);
                implicitActorInstantiations.Add(actorName, true);
            }
        }
Ejemplo n.º 4
0
 private static void enqueueRotateActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction,
                                          CM.Animation effectingAnimation, FireBoltActionList aaq,
                                          string parentActionId, bool implicitActorInstantiation)
 {
     foreach (CM.RotateAction ra in domainAction.RotateActions)
     {
         float   startTick     = 0;
         float   endTick       = 0;
         string  actorName     = null;
         float?  targetDegrees = null;
         Vector2?targetPoint   = null;
         foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
         {
             if (domainActionParameter.Name == ra.ActorNameParamName)
             {
                 if (!getActionParameterValue(storyAction, domainActionParameter, out actorName) ||
                     (implicitActorInstantiation && !actorWillBeInstantiated(actorName)))
                 {
                     break;
                 }
             }
             else if (domainActionParameter.Name == ra.DestinationParamName)
             {
                 IActionProperty targetOrientation;
                 if (storyAction.TryGetProperty(domainActionParameter.Name, out targetOrientation))
                 {
                     if (targetOrientation.Value.Value is float)
                     {
                         targetDegrees = (float)targetOrientation.Value.Value;
                         if (targetOrientation.Range.Name == "x+degrees")
                         {
                             targetDegrees = targetDegrees.Value.convertSourceEngineToUnityRotation();
                         }
                     }
                     else if (targetOrientation.Value.Value is Coordinate2D)
                     {
                         targetPoint = new Vector2((float)((Coordinate2D)targetOrientation.Value.Value).X,
                                                   (float)((Coordinate2D)targetOrientation.Value.Value).Y);
                     }
                 }
                 else
                 {
                     Debug.LogError("orientation not set for stepId[" + storyAction.Name + "]");
                 }
             }
         }
         startTick = getStartTick(storyAction, ra, effectingAnimation);
         endTick   = getEndTick(storyAction, ra, effectingAnimation, startTick);
         var targetRotation = new Vector3Nullable(null, targetDegrees, null);
         if (Rotate.ValidForConstruction(actorName, targetRotation, targetPoint))
         {
             aaq.Add(new Rotate(startTick, endTick, actorName, targetRotation, targetPoint)
             {
                 ParentActionId = parentActionId
             });
         }
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="storyPlanPath">path to the story plan to load</param>
        /// <param name="cinematicModelPath">path to the cinematic model to load</param>
        /// <returns></returns>
        public static FireBoltActionList CreateStoryActions(Story <UintV, UintT, IIntervalSet <UintV, UintT> > story, CM.CinematicModel cm, bool implicitActorCreation)
        {
            ActorActionFactory.cm = cm;
            FireBoltActionList aaq = new FireBoltActionList();

            ActorActionFactory.story = story;
            orderedObjectSets        = story.ObjectSetGraph.ReverseTopologicalSort().ToArray();
            //orderedActionTypes = story.ActionTypeGraph.ReverseTopologicalSort().ToArray();

            implicitActorInstantiations = new Dictionary <string, bool>();

            //buildInitialState(aaq);
            if (implicitActorCreation)
            {
                createActors(aaq);
            }

            //generate FireBolt actions for the steps
            foreach (IStoryAction <UintT> storyAction in story.Actions.Values)
            {
                CM.DomainAction domainAction = getStoryDomainAction(storyAction);
                if (domainAction == null)
                {
                    continue;
                }

                CM.Animation effectingAnimation = getEffectingAnimation(storyAction, domainAction);

                enqueueCreateActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name);
                enqueueAnimateActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name, implicitActorCreation);
                enqueueDestroyActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name, implicitActorCreation);
                enqueuetranslateActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name, implicitActorCreation);
                enqueueRotateActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name, implicitActorCreation);
                enqueueAttachActions(storyAction, domainAction, effectingAnimation, aaq, storyAction.Name, implicitActorCreation);
            }
            return(aaq);
        }
Ejemplo n.º 6
0
        private static void enqueueCreateActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction,
                                                 CM.Animation effectingAnimation, FireBoltActionList aaq, string parentActionId)
        {
            foreach (CM.CreateAction ca in domainAction.CreateActions)
            {
                float   startTick   = 0;
                string  actorName   = null;
                Vector3 destination = new Vector3();
                Vector3?orientation = null;
                float   targetDegrees;
                CinematicModelMetaData metaData = new CinematicModelMetaData();

                foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
                {
                    if (domainActionParameter.Name == ca.ActorNameParamName)
                    {
                        if (getActionParameterValue(storyAction, domainActionParameter, out actorName))//actorName is defined, we can look up a model
                        {
                            if (!getActorMetaData(actorName, out metaData))
                            {
                                break; //we failed to find the actor within the hierarchy
                            }
                        }
                    }
                    else if (domainActionParameter.Name == ca.OriginParamName)
                    {
                        IActionProperty coord;
                        if (storyAction.TryGetProperty(domainActionParameter.Name, out coord))
                        {
                            if (coord.Value.Value is Coordinate2D)
                            {
                                destination = ((Coordinate2D)coord.Value.Value).ToVector3(cm.DomainDistancePerEngineDistanceX,
                                                                                          cm.DomainDistancePerEngineDistanceY,
                                                                                          cm.DomainDistancePerEngineDistanceZ);
                            }
                            else if (coord.Value.Value is Coordinate3D)
                            {
                                destination = ((Coordinate3D)coord.Value.Value).ToVector3(cm.DomainDistancePerEngineDistanceX,
                                                                                          cm.DomainDistancePerEngineDistanceY,
                                                                                          cm.DomainDistancePerEngineDistanceZ);
                            }
                        }
                        else
                        {
                            Debug.LogError("origin not set for stepId[" + storyAction.Name + "]");
                        }
                    }
                    else if (domainActionParameter.Name == ca.OrientationParamName)
                    {
                        IActionProperty orientationProperty;
                        if (storyAction.TryGetProperty(domainActionParameter.Name, out orientationProperty) &&
                            tryConvertOrientation(orientationProperty, out targetDegrees))
                        {
                            orientation = new Vector3(0, targetDegrees, 0);
                        }
                        else
                        {
                            Debug.LogError("origin not set for stepId[" + storyAction.Name + "]");
                        }
                    }
                }
                startTick = getStartTick(storyAction, ca, effectingAnimation);
                if (Create.ValidForConstruction(actorName, metaData.ModelName))
                {
                    aaq.Add(new Create(startTick, actorName, metaData.ModelName, destination, metaData, orientation)
                    {
                        ParentActionId = parentActionId
                    });
                }
            }
        }
Ejemplo n.º 7
0
        private static void enqueueAnimateActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction,
                                                  CM.Animation effectingAnimation, FireBoltActionList aaq,
                                                  string parentActionId, bool implicitActorInstantiation)
        {
            foreach (CM.AnimateAction animateAction in domainAction.AnimateActions)
            {
                string actorName                   = null;
                string abstractActorName           = null;
                float  startTick                   = 0;
                float  endTick                     = 0;
                CM.AnimationMapping animMapping    = null;
                CM.AnimationMapping stateMapping   = null;
                CM.Animation        animation      = null;
                CM.Animation        stateAnimation = new CM.Animation();


                string endName           = !string.IsNullOrEmpty(animateAction.End) ? animateAction.End : string.Empty;
                string animateActionName = animateAction.Name;

                //PURPOSE: if domain action parameter is the name of an animateAction for that domain action as defined in the cinematic model,
                //then use the parameter value as the animateAction name.
                // Used when a domain action has a variable for accepting an action to play, handy for spawn actions that require an initial state
                foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
                {
                    //endName = string.Empty; //The endName should not be string.Empty if the below propety is never true.
                    // Extensions.Log("beforeset: " + animateAction.Name + " " +  domainActionParameter.Name);
                    if (domainActionParameter.Name.Equals(animateAction.Name))
                    {
                        getActionParameterValue(storyAction, domainActionParameter, out animateActionName);
                        endName = animateActionName;
                        break;
                    }
                }

                foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
                {
                    if (domainActionParameter.Name == animateAction.ActorNameParamName)
                    {
                        if (getActionParameterValue(storyAction, domainActionParameter, out actorName) &&
                            (!implicitActorInstantiation || actorWillBeInstantiated(actorName)))
                        {
                            //abstractActorName = actorName;
                            if (!getAbstractActorName(actorName, out abstractActorName))
                            {
                                //can't figure out what this actor name is supposed to be.  don't try to animate it.
                                break;
                            }
                            else
                            {
                                //this forces all levels of hierarchy to implement all animations if we want them played
                                //we can't partially look up levels above for some things
                                //iterate back and forth between finding matching hierarchical parents and looking for mappings to alleviate
                                if (!getAnimationMapping(abstractActorName, animateActionName, out animMapping))
                                {
                                    Extensions.Log("cinematic model animation instance undefined for actor[" +
                                                   abstractActorName + "] animateAction[" + animateActionName + "]");
                                    break;
                                }
                            }

                            //we have a valid mapping, let's use it to find an animation for this actor
                            animation = cm.FindAnimation(animMapping.AnimationName);
                            if (animation == null)
                            {
                                Extensions.Log("animation name [{0}] undefined", animMapping.AnimationName);
                                break;
                            }

                            //end name is optional, we don't have to do this for the animation to be valid
                            if (!string.IsNullOrEmpty(endName))
                            {
                                getAnimationMapping(abstractActorName, endName, out stateMapping);
                                if (!(stateMapping == null))
                                {
                                    stateAnimation = cm.FindAnimation(stateMapping.AnimationName);

                                    if (stateAnimation == null)
                                    {
                                        Extensions.Log("state animation name [{0}] undefined", stateMapping.AnimationName);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                startTick = getStartTick(storyAction, animateAction, effectingAnimation);
                endTick   = getEndTick(storyAction, animateAction, effectingAnimation, startTick);

                if (AnimateMecanim.ValidForConstruction(actorName, animation))
                {
                    //   Extensions.Log("actor: " + actorName + " animMappingName: " + animMapping.AnimationName + " animateActionName: " + animMapping.AnimateActionName + " loop: " + animMapping.LoopAnimation);
                    aaq.Add(new AnimateMecanim(startTick, endTick, actorName, animation.FileName,
                                               animMapping.LoopAnimation, stateAnimation.FileName)
                    {
                        ParentActionId = parentActionId
                    });
                }
            }
        }
Ejemplo n.º 8
0
 private static void enqueuetranslateActions(IStoryAction <UintT> storyAction, CM.DomainAction domainAction,
                                             CM.Animation effectingAnimation, FireBoltActionList aaq,
                                             string parentActionId, bool implicitActorInstantiation)
 {
     foreach (CM.TranslateAction ta in domainAction.TranslateActions)
     {
         float           startTick   = 0;
         float           endTick     = 0;
         string          actorName   = null;
         Vector3Nullable destination = new Vector3Nullable(null, null, null);
         Vector3         origin      = Vector3.zero;
         foreach (CM.DomainActionParameter domainActionParameter in domainAction.Params)
         {
             if (domainActionParameter.Name == ta.OriginParamName)
             {
                 IActionProperty coord;
                 if (storyAction.TryGetProperty(domainActionParameter.Name, out coord))
                 {
                     if (coord.Value.Value is Coordinate2D)
                     {
                         origin = ((Coordinate2D)coord.Value.Value).ToVector3(cm.DomainDistancePerEngineDistanceX, cm.DomainDistancePerEngineDistanceY, cm.DomainDistancePerEngineDistanceZ);
                     }
                     else if (coord.Value.Value is Coordinate3D)
                     {
                         origin = ((Coordinate3D)coord.Value.Value).ToVector3(cm.DomainDistancePerEngineDistanceX, cm.DomainDistancePerEngineDistanceY, cm.DomainDistancePerEngineDistanceZ);
                     }
                 }
                 else
                 {
                     Debug.LogError("origin not set for stepId[" + storyAction.Name + "]");
                 }
             }
             else if (domainActionParameter.Name == ta.DestinationParamName)
             {
                 IActionProperty coord;
                 if (storyAction.TryGetProperty(domainActionParameter.Name, out coord))
                 {
                     if (coord.Value.Value is Coordinate2D)
                     {
                         destination = ((Coordinate2D)coord.Value.Value).ToVector3Nullable(cm.DomainDistancePerEngineDistanceX, cm.DomainDistancePerEngineDistanceY, cm.DomainDistancePerEngineDistanceZ);
                     }
                     else if (coord.Value.Value is Coordinate3D)
                     {
                         destination = ((Coordinate3D)coord.Value.Value).ToVector3Nullable(cm.DomainDistancePerEngineDistanceX, cm.DomainDistancePerEngineDistanceY, cm.DomainDistancePerEngineDistanceZ);
                     }
                 }
                 else
                 {
                     Debug.LogError("destination not set for stepId[" + storyAction.Name + "]");
                 }
             }
             else if (domainActionParameter.Name == ta.ActorNameParamName)
             {
                 if (!getActionParameterValue(storyAction, domainActionParameter, out actorName) ||
                     (implicitActorInstantiation && !actorWillBeInstantiated(actorName)))
                 {
                     break;
                 }
             }
         }
         startTick = getStartTick(storyAction, ta, effectingAnimation);
         endTick   = getEndTick(storyAction, ta, effectingAnimation, startTick);
         if (Translate.ValidForConstruction(actorName))
         {
             aaq.Add(new Translate(startTick, endTick, actorName, origin, destination)
             {
                 ParentActionId = parentActionId
             });
         }
     }
 }
Ejemplo n.º 9
0
        public static void CreateActions(Story <UintV, UintT, IIntervalSet <UintV, UintT> > story, CM.CinematicModel cinematicModel, string cameraPlanPath,
                                         out CameraActionList cameraActionList, out FireBoltActionList discourseActionList)
        {
            cameraActionList    = new CameraActionList();
            discourseActionList = new FireBoltActionList();
            CameraPlan cameraPlan = Oshmirto.Parser.Parse(cameraPlanPath);

            if (cinematicModel.MillisPerTick != 1)
            {
                scaleTime(cinematicModel.MillisPerTick, cameraPlan);
            }


            uint  currentDiscourseTime    = 0;
            float previousStoryTimeOffset = 0;

            foreach (Block block in cameraPlan.Blocks)
            {
                float blockStartTime = Single.MaxValue;
                float blockEndTime   = Single.MinValue;
                foreach (var fragment in block.ShotFragments)
                {
                    uint fragmentStartTime = currentDiscourseTime;//removing increment.  cameras always execute after discourse actions currentDiscourseTime++;
                    uint fragmentEndTime   = fragmentStartTime + fragment.Duration;

                    if (fragmentStartTime < blockStartTime) //assumes same time scale for discourse and story
                    {
                        blockStartTime = fragmentStartTime;
                    }
                    if (fragmentEndTime > blockEndTime)
                    {
                        blockEndTime = fragmentEndTime;
                    }

                    cameraActionList.Add(new ShotFragmentInit(fragmentStartTime, cameraRig, fragment.Anchor, fragment.Height, fragment.Pan,
                                                              fragment.Lens, fragment.FStop, fragment.Framings, fragment.Direction, fragment.Angle, fragment.FocusPosition));

                    var            movementStartTime = fragmentStartTime + 1; //force moves to sort after inits
                    RotateRelative rotateWith        = null;                  //collect all the relative rotations together for the camera so we can avoid representational nightmares
                    Rotate         rotateTo          = null;                  //similarly collect absolute rotations...eventually we should cross check and/or merge this stuff
                    foreach (var movement in fragment.CameraMovements)
                    {
                        switch (movement.Type)
                        {
                        case CameraMovementType.Dolly:
                            switch (movement.Directive)
                            {
                            case (CameraMovementDirective.With):
                                cameraActionList.Add(new TranslateRelative(movement.Subject, movementStartTime, fragmentEndTime, cameraRig, false, true, false));
                                break;

                            case (CameraMovementDirective.To):
                                Vector2 destination;
                                if (movement.Subject.TryParsePlanarCoords(out destination))
                                {
                                    cameraActionList.Add(new Translate(movementStartTime, fragmentEndTime, cameraRig,
                                                                       null, new Vector3Nullable(destination.x, null, destination.y)));
                                }
                                break;
                            }
                            break;

                        case CameraMovementType.Crane:
                            switch (movement.Directive)
                            {
                            case CameraMovementDirective.With:
                                break;

                            case CameraMovementDirective.To:
                                cameraActionList.Add(new Translate(movementStartTime, fragmentEndTime, cameraRig,
                                                                   null, new Vector3Nullable(null, float.Parse(movement.Subject), null)));
                                break;
                            }
                            break;

                        case CameraMovementType.Pan:
                            switch (movement.Directive)
                            {
                            case CameraMovementDirective.With:
                                if (rotateWith != null)
                                {
                                    rotateWith.AppendAxis(movement.Subject, cameraRig, true, false);
                                }
                                else
                                {
                                    rotateWith = new RotateRelative(movement.Subject, movementStartTime, fragmentEndTime, cameraRig,
                                                                    true, false);
                                }

                                break;

                            case CameraMovementDirective.To:
                                if (rotateTo != null)
                                {
                                    rotateTo.AppendAxisY(float.Parse(movement.Subject));
                                }
                                else
                                {
                                    rotateTo = new Rotate(movementStartTime, fragmentEndTime, cameraRig, new Vector3Nullable(null, float.Parse(movement.Subject), null), null);
                                }
                                break;
                            }
                            break;

                        case CameraMovementType.Tilt:
                            switch (movement.Directive)
                            {
                            case CameraMovementDirective.With:         // will this co-execute with pan-with? not currently
                                if (rotateWith != null)
                                {
                                    rotateWith.AppendAxis(movement.Subject, cameraRig, false, true);
                                }
                                else
                                {
                                    rotateWith = new RotateRelative(movement.Subject, movementStartTime, fragmentEndTime, cameraRig,
                                                                    false, true);
                                }
                                break;

                            case CameraMovementDirective.To:
                                if (rotateTo != null)
                                {
                                    rotateTo.AppendAxisX(float.Parse(movement.Subject));
                                }
                                else
                                {
                                    rotateTo = new Rotate(movementStartTime, fragmentEndTime, cameraRig, new Vector3Nullable(float.Parse(movement.Subject), null, null), null);
                                }
                                break;
                            }
                            break;

                        case CameraMovementType.Focus:
                            switch (movement.Directive)
                            {
                            case CameraMovementDirective.With:
                                cameraActionList.Add(new Focus(movementStartTime, fragmentEndTime, cameraName, movement.Subject, true));
                                break;
                            }
                            break;
                        }
                    }
                    if (rotateWith != null)
                    {
                        cameraActionList.Add(rotateWith);
                    }
                    if (rotateTo != null)
                    {
                        cameraActionList.Add(rotateTo);
                    }
                    // Shake it off
                    if (fragment.Shake > float.Epsilon)
                    {
                        cameraActionList.Add(new Shake(movementStartTime, fragmentEndTime, cameraName, fragment.Shake));
                    }

                    currentDiscourseTime = fragmentEndTime + 1; //set fragments to end and next to start on next tick
                }
                if (block.StoryTime.HasValue)
                {
                    float currentStoryTimeOffset = block.StoryTime.Value - blockStartTime;
                    discourseActionList.Add(new SetStoryTime(currentStoryTimeOffset, previousStoryTimeOffset, blockStartTime, blockEndTime));
                    previousStoryTimeOffset = block.StoryTime.Value;
                }
            }
            cameraActionList.EndDiscourseTime = currentDiscourseTime;
        }