Exemplo n.º 1
0
        private static ObjectAction GetShootBulletAction(
            Dictionary <string, DTDanmakuImage> spriteNameToImageDictionary,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            GuidGenerator guidGenerator)
        {
            IMathExpression initialShootCooldownInMillis = MathExpression.RandomInteger(2750);
            IMathExpression shootCooldownInMillis        = MathExpression.Add(1750, MathExpression.RandomInteger(1000));

            string cooldownVariableName    = guidGenerator.NextGuid();
            string childObjectTemplateName = guidGenerator.NextGuid();
            string enemyBulletSpriteName   = guidGenerator.NextGuid();

            IMathExpression facingAngleInMillidegrees = DTDanmakuMath.GetMovementDirectionInMillidegrees(
                currentX: MathExpression.XMillis(),
                currentY: MathExpression.YMillis(),
                desiredX: MathExpression.PlayerXMillis(),
                desiredY: MathExpression.Min(MathExpression.PlayerYMillis(), 300 * 1000));

            DTDanmakuMath.MathExpressionOffset deltaXAndY = DTDanmakuMath.GetOffset(
                millipixels: MathExpression.Constant(40000),
                movementDirectionInMillidegrees: facingAngleInMillidegrees);

            ObjectAction initialStartCooldownAction = ObjectAction.SetNumericVariable(cooldownVariableName, initialShootCooldownInMillis);
            ObjectAction startCooldownAction        = ObjectAction.SetNumericVariable(cooldownVariableName, shootCooldownInMillis);
            ObjectAction decrementCooldownAction    = ObjectAction.SetNumericVariable(cooldownVariableName, MathExpression.Subtract(MathExpression.Variable(cooldownVariableName), MathExpression.ElapsedMillisecondsPerIteration()));
            ObjectAction createBulletAction         = ObjectAction.SpawnChild(
                childXMillis: MathExpression.Add(MathExpression.XMillis(), deltaXAndY.DeltaXInMillipixels),
                childYMillis: MathExpression.Add(MathExpression.YMillis(), deltaXAndY.DeltaYInMillipixels),
                childObjectTemplateName: childObjectTemplateName,
                childInitialNumericVariables: null,
                childInitialBooleanVariables: null);

            List <ObjectBox> collisionBoxes = new List <ObjectBox>();

            collisionBoxes.Add(new ObjectBox(lowerXMillis: -4000, upperXMillis: 4000, lowerYMillis: -4000, upperYMillis: 4000));

            enemyObjectTemplates.Add(childObjectTemplateName,
                                     EnemyObjectTemplate.EnemyBullet(
                                         action: ObjectAction.Union(GetBulletMovementAction(guidGenerator: guidGenerator), GetBulletAnimationAction(guidGenerator: guidGenerator)),
                                         initialMilliHP: null,
                                         damageBoxes: null,
                                         collisionBoxes: collisionBoxes,
                                         spriteName: enemyBulletSpriteName));

            spriteNameToImageDictionary.Add(enemyBulletSpriteName, DTDanmakuImage.EliteSniperEnemyBullet);

            var createBulletWhenCooldownFinishedAction = ObjectAction.Condition(
                condition: BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(cooldownVariableName), MathExpression.Constant(0)),
                action: ObjectAction.Union(startCooldownAction, createBulletAction));

            return(ObjectAction.ConditionalNextAction(
                       currentAction: initialStartCooldownAction,
                       condition: BooleanExpression.True(),
                       nextAction: ObjectAction.Union(decrementCooldownAction, createBulletWhenCooldownFinishedAction)));
        }
Exemplo n.º 2
0
        public static void UpdateEnemyPositions(
            List <EnemyObject> enemyObjects,
            long elapsedMillisecondsPerIteration)
        {
            foreach (EnemyObject enemyObject in enemyObjects)
            {
                if (enemyObject.IsDestroyed)
                {
                    continue;
                }

                DTDanmakuMath.Offset offset = DTDanmakuMath.GetOffset(
                    speedInMillipixelsPerMillisecond: enemyObject.SpeedInMillipixelsPerMillisecond,
                    movementDirectionInMillidegrees: enemyObject.MovementDirectionInMillidegrees,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration);

                enemyObject.XMillis += offset.DeltaXInMillipixels;
                enemyObject.YMillis += offset.DeltaYInMillipixels;
            }
        }
Exemplo n.º 3
0
        private static Tuple <ObjectAction, BooleanExpression> MoveToSpecifiedLocations_Helper(
            long startingXMillis,
            long startingYMillis,
            long endingXMillis,
            long endingYMillis,
            long speedInPixelsPerSecond,
            bool shouldStrafe,
            GuidGenerator guidGenerator)
        {
            string variableName  = guidGenerator.NextGuid();
            string variableName2 = guidGenerator.NextGuid();

            long?direction = DTDanmakuMath.GetMovementDirectionInMillidegrees(
                currentX: startingXMillis,
                currentY: startingYMillis,
                desiredX: endingXMillis,
                desiredY: endingYMillis);

            if (direction == null)
            {
                throw new Exception();
            }

            DTDanmakuMath.Offset offset = DTDanmakuMath.GetOffset(
                speedInMillipixelsPerMillisecond: speedInPixelsPerSecond, // millipixels/millisecond is equivalent to pixels/second
                movementDirectionInMillidegrees: direction.Value,
                elapsedMillisecondsPerIteration: 1000);                   // Use the 1000 to help prevent rounding issues

            ObjectAction moveAction = ObjectAction.SetPosition(
                xMillis: MathExpression.Add(MathExpression.XMillis(), MathExpression.Divide(MathExpression.Multiply(MathExpression.Constant(offset.DeltaXInMillipixels), MathExpression.ElapsedMillisecondsPerIteration()), MathExpression.Constant(1000))),
                yMillis: MathExpression.Add(MathExpression.YMillis(), MathExpression.Divide(MathExpression.Multiply(MathExpression.Constant(offset.DeltaYInMillipixels), MathExpression.ElapsedMillisecondsPerIteration()), MathExpression.Constant(1000))));

            ObjectAction setDirectionAction = shouldStrafe
                                ? ObjectAction.SetFacingDirection(facingDirectionInMillidegrees: MathExpression.Constant(180 * 1000))
                                : ObjectAction.SetFacingDirection(facingDirectionInMillidegrees: MathExpression.Constant(direction.Value));

            IMathExpression deltaX             = MathExpression.AbsoluteValue(MathExpression.Subtract(MathExpression.XMillis(), MathExpression.Constant(endingXMillis)));
            IMathExpression deltaY             = MathExpression.AbsoluteValue(MathExpression.Subtract(MathExpression.YMillis(), MathExpression.Constant(endingYMillis)));
            IMathExpression totalDelta         = MathExpression.Add(deltaX, deltaY);
            ObjectAction    setDeltaToVariable = ObjectAction.Union(
                ObjectAction.SetNumericVariable(variableName: variableName2, variableValue: MathExpression.Variable(variableName)),
                ObjectAction.SetNumericVariable(variableName: variableName, variableValue: totalDelta));
            BooleanExpression reachedDestination = BooleanExpression.And(
                BooleanExpression.LessThanOrEqualTo(
                    MathExpression.Variable(variableName2),
                    MathExpression.Variable(variableName)),
                BooleanExpression.And(
                    BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(variableName), MathExpression.Constant(0)),
                    BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(variableName2), MathExpression.Constant(0))
                    ));

            ObjectAction initializeVariables = ObjectAction.Union(
                ObjectAction.SetNumericVariable(variableName: variableName, variableValue: MathExpression.Constant(-1)),
                ObjectAction.SetNumericVariable(variableName: variableName2, variableValue: MathExpression.Constant(-1)));

            return(new Tuple <ObjectAction, BooleanExpression>(
                       ObjectAction.Union(
                           moveAction,
                           setDirectionAction,
                           ObjectAction.ConditionalNextAction(
                               currentAction: initializeVariables,
                               condition: BooleanExpression.True(),
                               nextAction: setDeltaToVariable)),
                       reachedDestination));
        }
Exemplo n.º 4
0
        // Spawns 12 bullets (30 degrees apart)
        private static ObjectAction SpawnSatelliteBullet(
            Dictionary <string, DTDanmakuImage> spriteNameToImageDictionary,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            GuidGenerator guidGenerator)
        {
            string singleBulletSpriteName = guidGenerator.NextGuid();

            spriteNameToImageDictionary.Add(singleBulletSpriteName, DTDanmakuImage.EliteOrbiterEnemyBullet);

            long buffer = 100;

            BooleanExpression shouldDestroySingleBullet = BooleanExpression.Or(
                BooleanExpression.GreaterThan(MathExpression.XMillis(), MathExpression.Constant((1000 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.XMillis(), MathExpression.Constant((0 - buffer) * 1000)),
                BooleanExpression.GreaterThan(MathExpression.YMillis(), MathExpression.Constant((700 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.YMillis(), MathExpression.Constant((0 - buffer) * 1000)));

            ObjectAction destroySingleBulletAction = ObjectAction.Condition(
                condition: shouldDestroySingleBullet,
                action: ObjectAction.Destroy());

            DTDanmakuMath.MathExpressionOffset singleBulletOffset = DTDanmakuMath.GetOffset(
                millipixels: MathExpression.Multiply(MathExpression.Constant(100), MathExpression.ElapsedMillisecondsPerIteration()),
                movementDirectionInMillidegrees: MathExpression.Variable("direction"));

            List <ObjectBox> collisionBoxes = new List <ObjectBox>();

            collisionBoxes.Add(new ObjectBox(lowerXMillis: -3000, upperXMillis: 3000, lowerYMillis: -5000, upperYMillis: 5000));
            collisionBoxes.Add(new ObjectBox(lowerXMillis: -5000, upperXMillis: 5000, lowerYMillis: -3000, upperYMillis: 3000));

            EnemyObjectTemplate singleBulletTemplate = EnemyObjectTemplate.EnemyBullet(
                action: ObjectAction.Union(
                    ObjectAction.SetFacingDirection(MathExpression.Variable("direction")),
                    ObjectAction.SetPosition(
                        xMillis: MathExpression.Add(MathExpression.XMillis(), singleBulletOffset.DeltaXInMillipixels),
                        yMillis: MathExpression.Add(MathExpression.YMillis(), singleBulletOffset.DeltaYInMillipixels)),
                    destroySingleBulletAction),
                initialMilliHP: null,
                damageBoxes: null,
                collisionBoxes: collisionBoxes,
                spriteName: singleBulletSpriteName);

            string singleBulletTemplateName = guidGenerator.NextGuid();

            enemyObjectTemplates.Add(singleBulletTemplateName, singleBulletTemplate);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables1 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables1.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(0))));

            ObjectAction spawnSingleBulletAction1 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables1,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables2 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables2.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(30 * 1000))));

            ObjectAction spawnSingleBulletAction2 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables2,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables3 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables3.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(60 * 1000))));

            ObjectAction spawnSingleBulletAction3 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables3,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables4 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables4.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(90 * 1000))));

            ObjectAction spawnSingleBulletAction4 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables4,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables5 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables5.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(120 * 1000))));

            ObjectAction spawnSingleBulletAction5 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables5,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables6 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables6.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(150 * 1000))));

            ObjectAction spawnSingleBulletAction6 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables6,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables7 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables7.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(180 * 1000))));

            ObjectAction spawnSingleBulletAction7 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables7,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables8 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables8.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(210 * 1000))));

            ObjectAction spawnSingleBulletAction8 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables8,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables9 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables9.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(240 * 1000))));

            ObjectAction spawnSingleBulletAction9 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables9,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables10 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables10.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(270 * 1000))));

            ObjectAction spawnSingleBulletAction10 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables10,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables11 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables11.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(300 * 1000))));

            ObjectAction spawnSingleBulletAction11 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables11,
                childInitialBooleanVariables: null);

            List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables12 = new List <ObjectAction.InitialChildNumericVariableInfo>();

            initialNumericVariables12.Add(new ObjectAction.InitialChildNumericVariableInfo(name: "direction", value: MathExpression.Add(MathExpression.Variable("random offset"), MathExpression.Constant(330 * 1000))));

            ObjectAction spawnSingleBulletAction12 = ObjectAction.SpawnChild(
                childXMillis: MathExpression.XMillis(),
                childYMillis: MathExpression.YMillis(),
                childObjectTemplateName: singleBulletTemplateName,
                childInitialNumericVariables: initialNumericVariables12,
                childInitialBooleanVariables: null);

            List <ObjectAction> actionList = new List <ObjectAction>();

            actionList.Add(ObjectAction.SetNumericVariable("random offset", MathExpression.RandomInteger(MathExpression.Constant(360 * 1000))));
            actionList.Add(spawnSingleBulletAction1);
            actionList.Add(spawnSingleBulletAction2);
            actionList.Add(spawnSingleBulletAction3);
            actionList.Add(spawnSingleBulletAction4);
            actionList.Add(spawnSingleBulletAction5);
            actionList.Add(spawnSingleBulletAction6);
            actionList.Add(spawnSingleBulletAction7);
            actionList.Add(spawnSingleBulletAction8);
            actionList.Add(spawnSingleBulletAction9);
            actionList.Add(spawnSingleBulletAction10);
            actionList.Add(spawnSingleBulletAction11);
            actionList.Add(spawnSingleBulletAction12);
            actionList.Add(ObjectAction.Destroy());

            EnemyObjectTemplate placeholderTemplate = EnemyObjectTemplate.Placeholder(
                action: ObjectAction.Union(actionList));

            string placeholderTemplateName = guidGenerator.NextGuid();

            enemyObjectTemplates.Add(placeholderTemplateName, placeholderTemplate);

            return(ObjectAction.SpawnChild(
                       childXMillis: MathExpression.XMillis(),
                       childYMillis: MathExpression.YMillis(),
                       childObjectTemplateName: placeholderTemplateName,
                       childInitialNumericVariables: null,
                       childInitialBooleanVariables: null));
        }
Exemplo n.º 5
0
        private static ObjectAction GetMoveAction(
            GuidGenerator guidGenerator)
        {
            string movementCooldownVariable = guidGenerator.NextGuid();

            ObjectAction setMovementCooldownAction = ObjectAction.SetNumericVariable(
                movementCooldownVariable,
                MathExpression.Add(MathExpression.Constant(500), MathExpression.RandomInteger(5000)));

            ObjectAction decrementMovementCooldownAction = ObjectAction.SetNumericVariable(
                movementCooldownVariable,
                MathExpression.Subtract(MathExpression.Variable(movementCooldownVariable), MathExpression.ElapsedMillisecondsPerIteration()));

            BooleanExpression shouldChangeMovement = BooleanExpression.LessThanOrEqualTo(
                MathExpression.Variable(movementCooldownVariable),
                MathExpression.Constant(0));

            IMathExpression randomMilliAngle = MathExpression.RandomInteger(MathExpression.Constant(360 * 1000));

            string       angleVariable           = guidGenerator.NextGuid();
            ObjectAction writeNewAngleToVariable = ObjectAction.SetNumericVariable(
                angleVariable,
                randomMilliAngle);

            ObjectAction normalizeAngleVariable = ObjectAction.Union(
                ObjectAction.Condition(
                    condition: BooleanExpression.LessThan(MathExpression.Variable(angleVariable), MathExpression.Constant(0)),
                    action: ObjectAction.SetNumericVariable(angleVariable, MathExpression.Add(MathExpression.Variable(angleVariable), MathExpression.Constant(360 * 1000)))),
                ObjectAction.Condition(
                    condition: BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(360 * 1000)),
                    action: ObjectAction.SetNumericVariable(angleVariable, MathExpression.Subtract(MathExpression.Variable(angleVariable), MathExpression.Constant(360 * 1000)))));

            BooleanExpression tooCloseToLeftEdge = BooleanExpression.LessThanOrEqualTo(
                MathExpression.XMillis(),
                MathExpression.Constant(200 * 1000));

            ObjectAction updateAngleVariableLeft = ObjectAction.Condition(
                condition: BooleanExpression.And(tooCloseToLeftEdge, BooleanExpression.And(
                                                     BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(180 * 1000)),
                                                     BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(360 * 1000)))),
                action: ObjectAction.SetNumericVariable(
                    angleVariable,
                    MathExpression.Multiply(MathExpression.Variable(angleVariable), MathExpression.Constant(-1))));

            BooleanExpression tooCloseToRightEdge = BooleanExpression.GreaterThanOrEqualTo(
                MathExpression.XMillis(),
                MathExpression.Constant(800 * 1000));

            ObjectAction updateAngleVariableRight = ObjectAction.Condition(
                condition: BooleanExpression.And(tooCloseToRightEdge, BooleanExpression.And(
                                                     BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(0 * 1000)),
                                                     BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(180 * 1000)))),
                action: ObjectAction.SetNumericVariable(
                    angleVariable,
                    MathExpression.Multiply(MathExpression.Variable(angleVariable), MathExpression.Constant(-1))));

            BooleanExpression tooCloseToTopEdge = BooleanExpression.GreaterThanOrEqualTo(
                MathExpression.YMillis(),
                MathExpression.Constant(600 * 1000));

            ObjectAction updateAngleVariableTop = ObjectAction.Condition(
                condition: BooleanExpression.And(tooCloseToTopEdge, BooleanExpression.Or(
                                                     BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(270 * 1000)),
                                                     BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(90 * 1000)))),
                action: ObjectAction.SetNumericVariable(
                    angleVariable,
                    MathExpression.Subtract(
                        MathExpression.Multiply(MathExpression.Add(MathExpression.Variable(angleVariable), MathExpression.Constant(90 * 1000)), MathExpression.Constant(-1)),
                        MathExpression.Constant(90 * 1000))));

            BooleanExpression tooCloseToBottomEdge = BooleanExpression.LessThanOrEqualTo(
                MathExpression.YMillis(),
                MathExpression.Constant(400 * 1000));

            ObjectAction updateAngleVariableBottom = ObjectAction.Condition(
                condition: BooleanExpression.And(tooCloseToBottomEdge, BooleanExpression.And(
                                                     BooleanExpression.GreaterThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(90 * 1000)),
                                                     BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(angleVariable), MathExpression.Constant(270 * 1000)))),
                action: ObjectAction.SetNumericVariable(
                    angleVariable,
                    MathExpression.Subtract(
                        MathExpression.Multiply(MathExpression.Add(MathExpression.Variable(angleVariable), MathExpression.Constant(90 * 1000)), MathExpression.Constant(-1)),
                        MathExpression.Constant(90 * 1000))));

            string angleSnapshotVariable = guidGenerator.NextGuid();

            ObjectAction initializeAngleSnapshotVariable = ObjectActionGenerator.DoOnce(
                ObjectAction.SetNumericVariable(angleSnapshotVariable, MathExpression.RandomInteger(360 * 1000)));

            DTDanmakuMath.MathExpressionOffset offset = DTDanmakuMath.GetOffset(
                millipixels: MathExpression.Multiply(MathExpression.Constant(80), MathExpression.ElapsedMillisecondsPerIteration()),
                movementDirectionInMillidegrees: MathExpression.Variable(angleSnapshotVariable));

            ObjectAction updatePositionAction = ObjectAction.SetPosition(
                xMillis: MathExpression.Add(MathExpression.XMillis(), offset.DeltaXInMillipixels),
                yMillis: MathExpression.Add(MathExpression.YMillis(), offset.DeltaYInMillipixels));

            ObjectAction updatePositionWhenCooldownIsZero = ObjectAction.Condition(
                condition: shouldChangeMovement,
                action: ObjectAction.Union(
                    ObjectAction.SetNumericVariable(angleSnapshotVariable, MathExpression.Variable(angleVariable)),
                    setMovementCooldownAction));

            ObjectAction immediateMoveWhenTooClose = ObjectAction.Condition(
                condition: BooleanExpression.Or(tooCloseToLeftEdge, tooCloseToRightEdge, tooCloseToTopEdge, tooCloseToBottomEdge),
                action: ObjectAction.SetNumericVariable(movementCooldownVariable, MathExpression.Constant(0)));

            List <ObjectAction> actionList = new List <ObjectAction>();

            actionList.Add(initializeAngleSnapshotVariable);
            actionList.Add(ObjectActionGenerator.DoOnce(setMovementCooldownAction));
            actionList.Add(decrementMovementCooldownAction);
            actionList.Add(writeNewAngleToVariable);
            actionList.Add(updateAngleVariableLeft);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(updateAngleVariableRight);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(updateAngleVariableTop);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(updateAngleVariableBottom);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(normalizeAngleVariable);
            actionList.Add(updatePositionWhenCooldownIsZero);
            actionList.Add(updatePositionAction);
            actionList.Add(immediateMoveWhenTooClose);

            return(ObjectAction.Union(actionList));
        }
Exemplo n.º 6
0
        private static ObjectAction Phase1ShootAction(
            Dictionary <string, DTDanmakuImage> spriteNameToImageDictionary,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            GuidGenerator guidGenerator)
        {
            string singleBulletSpriteName = guidGenerator.NextGuid();

            spriteNameToImageDictionary.Add(singleBulletSpriteName, DTDanmakuImage.BossPhase1EnemyBullet);

            long buffer = 100;

            BooleanExpression shouldDestroySingleBullet = BooleanExpression.Or(
                BooleanExpression.GreaterThan(MathExpression.XMillis(), MathExpression.Constant((1000 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.XMillis(), MathExpression.Constant((0 - buffer) * 1000)),
                BooleanExpression.GreaterThan(MathExpression.YMillis(), MathExpression.Constant((700 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.YMillis(), MathExpression.Constant((0 - buffer) * 1000)));

            ObjectAction destroySingleBulletAction = ObjectAction.Condition(
                condition: shouldDestroySingleBullet,
                action: ObjectAction.Destroy());

            DTDanmakuMath.MathExpressionOffset singleBulletOffset = DTDanmakuMath.GetOffset(
                millipixels: MathExpression.Multiply(MathExpression.Variable("speed"), MathExpression.ElapsedMillisecondsPerIteration()),
                movementDirectionInMillidegrees: MathExpression.Variable("direction"));

            List <ObjectBox> collisionBoxes = new List <ObjectBox>();

            collisionBoxes.Add(new ObjectBox(lowerXMillis: -3000, upperXMillis: 3000, lowerYMillis: -5000, upperYMillis: 5000));
            collisionBoxes.Add(new ObjectBox(lowerXMillis: -5000, upperXMillis: 5000, lowerYMillis: -3000, upperYMillis: 3000));

            EnemyObjectTemplate singleBulletTemplate = EnemyObjectTemplate.EnemyBullet(
                action: ObjectAction.Union(
                    ObjectAction.SetFacingDirection(MathExpression.Variable("direction")),
                    ObjectAction.SetPosition(
                        xMillis: MathExpression.Add(MathExpression.XMillis(), singleBulletOffset.DeltaXInMillipixels),
                        yMillis: MathExpression.Add(MathExpression.YMillis(), singleBulletOffset.DeltaYInMillipixels)),
                    destroySingleBulletAction),
                initialMilliHP: null,
                damageBoxes: null,
                collisionBoxes: collisionBoxes,
                spriteName: singleBulletSpriteName);

            string singleBulletTemplateName = guidGenerator.NextGuid();

            enemyObjectTemplates.Add(singleBulletTemplateName, singleBulletTemplate);

            List <ObjectAction> spawnBulletActions = new List <ObjectAction>();

            string randomOffsetVariable = guidGenerator.NextGuid();

            for (int i = 0; i < 36; i++)
            {
                List <ObjectAction.InitialChildNumericVariableInfo> initialNumericVariables = new List <ObjectAction.InitialChildNumericVariableInfo>();
                initialNumericVariables.Add(
                    new ObjectAction.InitialChildNumericVariableInfo(
                        name: "direction",
                        value: MathExpression.Add(MathExpression.Variable(randomOffsetVariable), MathExpression.Constant(i * 10 * 1000))));
                initialNumericVariables.Add(
                    new ObjectAction.InitialChildNumericVariableInfo(
                        name: "speed",
                        value: MathExpression.Add(MathExpression.Constant(100), MathExpression.RandomInteger(100))));

                spawnBulletActions.Add(ObjectAction.SpawnChild(
                                           childXMillis: MathExpression.XMillis(),
                                           childYMillis: MathExpression.Add(MathExpression.YMillis(), MathExpression.Constant(-60000)),
                                           childObjectTemplateName: singleBulletTemplateName,
                                           childInitialNumericVariables: initialNumericVariables,
                                           childInitialBooleanVariables: null));
            }

            List <ObjectAction> actionList = new List <ObjectAction>();

            actionList.Add(ObjectAction.SetNumericVariable(randomOffsetVariable, MathExpression.RandomInteger(MathExpression.Constant(360 * 1000))));
            foreach (ObjectAction spawnBulletAction in spawnBulletActions)
            {
                actionList.Add(spawnBulletAction);
            }

            ObjectAction shootBulletAction = ObjectAction.Union(actionList);

            string shootCooldown = guidGenerator.NextGuid();

            ObjectAction initializeShootCooldown = ObjectAction.SetNumericVariable(
                variableName: shootCooldown,
                variableValue: MathExpression.Constant(2000));

            ObjectAction decrementShootCooldown = ObjectAction.SetNumericVariable(
                variableName: shootCooldown,
                variableValue: MathExpression.Subtract(MathExpression.Variable(shootCooldown), MathExpression.ElapsedMillisecondsPerIteration()));

            ObjectAction shootWhenCooldownIsZero = ObjectAction.Condition(
                condition: BooleanExpression.LessThanOrEqualTo(MathExpression.Variable(shootCooldown), MathExpression.Constant(0)),
                action: ObjectAction.Union(initializeShootCooldown, shootBulletAction));

            return(ObjectAction.Union(
                       ObjectActionGenerator.DoOnce(initializeShootCooldown),
                       decrementShootCooldown,
                       shootWhenCooldownIsZero));
        }
Exemplo n.º 7
0
        private static ObjectAction SpawnPhase3Bullet(
            IMathExpression bulletDirectionInMillidegrees,
            Dictionary <string, DTDanmakuImage> spriteNameToImageDictionary,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            GuidGenerator guidGenerator)
        {
            string bulletSpriteName = guidGenerator.NextGuid();

            spriteNameToImageDictionary.Add(bulletSpriteName, DTDanmakuImage.BossPhase3EnemyBullet);

            string snapshotBulletDirectionInMillidegreesVariable = guidGenerator.NextGuid();

            ObjectAction snapshotDirectionAction = ObjectActionGenerator.DoOnce(ObjectAction.SetNumericVariable(
                                                                                    snapshotBulletDirectionInMillidegreesVariable,
                                                                                    bulletDirectionInMillidegrees));

            long buffer = 100;

            BooleanExpression shouldDestroy = BooleanExpression.Or(
                BooleanExpression.GreaterThan(MathExpression.XMillis(), MathExpression.Constant((1000 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.XMillis(), MathExpression.Constant((0 - buffer) * 1000)),
                BooleanExpression.GreaterThan(MathExpression.YMillis(), MathExpression.Constant((700 + buffer) * 1000)),
                BooleanExpression.LessThan(MathExpression.YMillis(), MathExpression.Constant((0 - buffer) * 1000)));

            ObjectAction destroyAction = ObjectAction.Condition(
                condition: shouldDestroy,
                action: ObjectAction.Destroy());

            DTDanmakuMath.MathExpressionOffset offset = DTDanmakuMath.GetOffset(
                millipixels: MathExpression.Multiply(MathExpression.Constant(200), MathExpression.ElapsedMillisecondsPerIteration()),
                movementDirectionInMillidegrees: MathExpression.Variable(snapshotBulletDirectionInMillidegreesVariable));

            List <ObjectBox> collisionBoxes = new List <ObjectBox>();

            collisionBoxes.Add(new ObjectBox(lowerXMillis: -3000, upperXMillis: 3000, lowerYMillis: -5000, upperYMillis: 5000));
            collisionBoxes.Add(new ObjectBox(lowerXMillis: -5000, upperXMillis: 5000, lowerYMillis: -3000, upperYMillis: 3000));

            EnemyObjectTemplate bulletTemplate = EnemyObjectTemplate.EnemyBullet(
                action: ObjectAction.Union(
                    snapshotDirectionAction,
                    ObjectAction.SetFacingDirection(MathExpression.Variable(snapshotBulletDirectionInMillidegreesVariable)),
                    ObjectAction.SetPosition(
                        xMillis: MathExpression.Add(MathExpression.XMillis(), offset.DeltaXInMillipixels),
                        yMillis: MathExpression.Add(MathExpression.YMillis(), offset.DeltaYInMillipixels)),
                    destroyAction),
                initialMilliHP: null,
                damageBoxes: null,
                collisionBoxes: collisionBoxes,
                spriteName: bulletSpriteName);

            string templateName = guidGenerator.NextGuid();

            enemyObjectTemplates.Add(templateName, bulletTemplate);

            return(ObjectAction.SpawnChild(
                       childXMillis: MathExpression.XMillis(),
                       childYMillis: MathExpression.Subtract(MathExpression.YMillis(), MathExpression.Constant(60000)),
                       childObjectTemplateName: templateName,
                       childInitialNumericVariables: null,
                       childInitialBooleanVariables: null));
        }
Exemplo n.º 8
0
        public static UpdateResult Update(
            List <EnemyObject> currentEnemyObjects,
            long playerXMillis,
            long playerYMillis,
            long elapsedMillisecondsPerIteration,
            bool isPlayerDestroyed,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            IDTDeterministicRandom rng)
        {
            List <EnemyObjectWrapper> wrapperList = new List <EnemyObjectWrapper>();

            foreach (EnemyObject enemyObj in currentEnemyObjects)
            {
                wrapperList.Add(new EnemyObjectWrapper(enemyObj, hasHandledAction: false));
            }

            var newPowerUps           = new List <Tuple <long, long> >();
            var newSoundEffectsToPlay = new List <string>();

            long?bossHealthMeterNumber          = null;
            long?bossHealthMeterMilliPercentage = null;

            bool shouldEndLevel = false;

            while (true)
            {
                bool hasHandledAllActions = true;
                foreach (EnemyObjectWrapper x in wrapperList)
                {
                    if (!x.HasHandledAction)
                    {
                        hasHandledAllActions = false;
                        break;
                    }
                }

                if (hasHandledAllActions)
                {
                    break;
                }

                var newEnemyObjects = new List <EnemyObject>();

                foreach (EnemyObjectWrapper enemyObjectWrapper in wrapperList)
                {
                    if (enemyObjectWrapper.HasHandledAction)
                    {
                        continue;
                    }

                    enemyObjectWrapper.HasHandledAction = true;

                    EnemyObject enemyObject = enemyObjectWrapper.EnemyObject;

                    if (enemyObject.IsDestroyed)
                    {
                        continue;
                    }

                    ResultOfAction actionResult = HandleAction(
                        action: enemyObject.Action,
                        obj: enemyObject,
                        playerXMillis: playerXMillis,
                        playerYMillis: playerYMillis,
                        elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                        isPlayerDestroyed: isPlayerDestroyed,
                        enemyObjectTemplates: enemyObjectTemplates,
                        rng: rng);

                    enemyObject.Action = actionResult.NewObjectAction;

                    if (actionResult.ShouldEndLevel)
                    {
                        shouldEndLevel = true;
                    }

                    foreach (EnemyObject newEnemy in actionResult.NewEnemyObjects)
                    {
                        newEnemyObjects.Add(newEnemy);
                    }

                    foreach (var newPowerUp in actionResult.NewPowerUps)
                    {
                        newPowerUps.Add(newPowerUp);
                    }

                    foreach (var newSoundEffect in actionResult.NewSoundEffectsToPlay)
                    {
                        newSoundEffectsToPlay.Add(newSoundEffect);
                    }

                    if (actionResult.BossHealthMeterNumber != null)
                    {
                        bossHealthMeterNumber = actionResult.BossHealthMeterNumber;
                    }

                    if (actionResult.BossHealthMeterMilliPercentage != null)
                    {
                        bossHealthMeterMilliPercentage = actionResult.BossHealthMeterMilliPercentage;
                    }

                    if (!enemyObject.IsDestroyed)
                    {
                        var offset = DTDanmakuMath.GetOffset(
                            speedInMillipixelsPerMillisecond: enemyObject.SpeedInMillipixelsPerMillisecond,
                            movementDirectionInMillidegrees: enemyObject.MovementDirectionInMillidegrees,
                            elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration);

                        enemyObject.XMillis += offset.DeltaXInMillipixels;
                        enemyObject.YMillis += offset.DeltaYInMillipixels;
                    }
                }

                foreach (EnemyObject newEnemyObject in newEnemyObjects)
                {
                    wrapperList.Add(new EnemyObjectWrapper(newEnemyObject, hasHandledAction: false));
                }
            }

            var finalEnemyList = new List <EnemyObject>();

            foreach (EnemyObjectWrapper enemyObjectWrapper in wrapperList)
            {
                if (!enemyObjectWrapper.EnemyObject.IsDestroyed)
                {
                    finalEnemyList.Add(enemyObjectWrapper.EnemyObject);
                }
            }

            return(new UpdateResult
            {
                NewEnemyObjects = finalEnemyList,
                NewPowerUps = newPowerUps,
                ShouldEndLevel = shouldEndLevel,
                NewSoundEffectsToPlay = newSoundEffectsToPlay,
                BossHealthMeterNumber = bossHealthMeterNumber,
                BossHealthMeterMilliPercentage = bossHealthMeterMilliPercentage
            });
        }
Exemplo n.º 9
0
        private static ResultOfAction HandleAction(
            ObjectAction action,
            EnemyObject obj,
            long playerXMillis,
            long playerYMillis,
            long elapsedMillisecondsPerIteration,
            bool isPlayerDestroyed,
            Dictionary <string, EnemyObjectTemplate> enemyObjectTemplates,
            IDTDeterministicRandom rng)
        {
            bool?isParentDestroyed;

            if (obj.ParentObject == null)
            {
                isParentDestroyed = null;
            }
            else
            {
                isParentDestroyed = obj.ParentObject.IsDestroyed;
            }

            switch (action.ObjectActionType)
            {
            case ObjectAction.Type.Move:
            case ObjectAction.Type.StrafeMove:
                long desiredX = action.MoveToXMillis.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);
                long desiredY = action.MoveToYMillis.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);
                long?directionInMillidegrees = DTDanmakuMath.GetMovementDirectionInMillidegrees(currentX: obj.XMillis, currentY: obj.YMillis, desiredX: desiredX, desiredY: desiredY);

                if (directionInMillidegrees != null)
                {
                    obj.MovementDirectionInMillidegrees = directionInMillidegrees.Value;
                    if (action.ObjectActionType == ObjectAction.Type.Move)
                    {
                        obj.FacingDirectionInMillidegrees = directionInMillidegrees.Value;
                    }
                    else if (action.ObjectActionType == ObjectAction.Type.StrafeMove)
                    {
                        obj.FacingDirectionInMillidegrees = 180L * 1000L;
                    }
                    else
                    {
                        throw new Exception();
                    }
                }

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetSpeed:
            case ObjectAction.Type.IncreaseSpeed:
            case ObjectAction.Type.DecreaseSpeed:
                long speed = action.SpeedInMillipixelsPerMillisecond.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                if (action.ObjectActionType == ObjectAction.Type.SetSpeed)
                {
                    obj.SpeedInMillipixelsPerMillisecond = speed;
                }
                else if (action.ObjectActionType == ObjectAction.Type.IncreaseSpeed)
                {
                    obj.SpeedInMillipixelsPerMillisecond += speed;
                }
                else if (action.ObjectActionType == ObjectAction.Type.DecreaseSpeed)
                {
                    obj.SpeedInMillipixelsPerMillisecond -= speed;
                }
                else
                {
                    throw new Exception();
                }

                if (obj.SpeedInMillipixelsPerMillisecond < 0)
                {
                    obj.SpeedInMillipixelsPerMillisecond = 0;
                }

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetPosition:
                long newXMillisPosition = action.SetXMillisPosition.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);
                long newYMillisPosition = action.SetYMillisPosition.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);
                obj.XMillis = newXMillisPosition;
                obj.YMillis = newYMillisPosition;

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetFacingDirection:

                long newFacingDirection = action.SetFacingDirectionInMillidegrees.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                obj.FacingDirectionInMillidegrees = newFacingDirection;

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.Destroy:
                obj.IsDestroyed = true;

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.DestroyParent:
                if (obj.ParentObject == null)
                {
                    throw new Exception();
                }

                obj.ParentObject.IsDestroyed = true;

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SpawnChild:
                long childXMillis = action.SpawnChildXMillis.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);
                long childYMillis = action.SpawnChildYMillis.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                EnemyObjectTemplate childObjectTemplate = enemyObjectTemplates[action.SpawnChildObjectTemplateName];

                var childObjectNumericVariables = new Dictionary <string, IMathExpression>();
                var childObjectBooleanVariables = new Dictionary <string, BooleanExpression>();

                if (action.SpawnChildInitialChildNumericVariables != null)
                {
                    for (int i = 0; i < action.SpawnChildInitialChildNumericVariables.Count; i++)
                    {
                        childObjectNumericVariables.Add(action.SpawnChildInitialChildNumericVariables[i].Name, action.SpawnChildInitialChildNumericVariables[i].Value);
                    }
                }
                if (action.SpawnChildInitialChildBooleanVariables != null)
                {
                    for (int i = 0; i < action.SpawnChildInitialChildBooleanVariables.Count; i++)
                    {
                        childObjectBooleanVariables.Add(action.SpawnChildInitialChildBooleanVariables[i].Name, action.SpawnChildInitialChildBooleanVariables[i].Value);
                    }
                }

                var newEnemyObjects = new List <EnemyObject>();

                newEnemyObjects.Add(new EnemyObject(
                                        template: childObjectTemplate,
                                        initialXMillis: childXMillis,
                                        initialYMillis: childYMillis,
                                        playerXMillis: playerXMillis,
                                        playerYMillis: playerYMillis,
                                        elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                                        isPlayerDestroyed: isPlayerDestroyed,
                                        parent: obj,
                                        initialNumericVariables: childObjectNumericVariables,
                                        initialBooleanVariables: childObjectBooleanVariables,
                                        rng: rng));

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: newEnemyObjects,
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SpawnPowerUp:
                var powerUpList = new List <Tuple <long, long> >();
                powerUpList.Add(new Tuple <long, long>(obj.XMillis, obj.YMillis));

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: powerUpList,
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetNumericVariable:

                obj.NumericVariables[action.SetVariableName] = action.SetNumericVariableValue.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetBooleanVariable:
                obj.BooleanVariables[action.SetVariableName] = action.SetBooleanVariableValue.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    isParentDestroyed: isParentDestroyed,
                    isPlayerDestroyed: isPlayerDestroyed,
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetParentNumericVariable:

                if (obj.ParentObject == null)
                {
                    throw new Exception();
                }

                obj.ParentObject.NumericVariables[action.SetVariableName] = action.SetNumericVariableValue.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.SetParentBooleanVariable:
            {
                if (obj.ParentObject == null)
                {
                    throw new Exception();
                }

                obj.ParentObject.BooleanVariables[action.SetVariableName] = action.SetBooleanVariableValue.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    isParentDestroyed: obj.ParentObject.IsDestroyed,
                    isPlayerDestroyed: isPlayerDestroyed,
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));
            }

            case ObjectAction.Type.EndLevel:

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: true,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.PlaySoundEffect:
            {
                var newSoundEffectsToPlay = new List <string>();
                newSoundEffectsToPlay.Add(action.SoundEffectName);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: newSoundEffectsToPlay,
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));
            }

            case ObjectAction.Type.DisplayBossHealthBar:
            {
                long bossHealthMeterNumber = action.BossHealthBarMeterNumber.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                long bossHealthMeterMilliPercentage = action.BossHealthBarMilliPercentage.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: bossHealthMeterNumber,
                           bossHealthMeterMilliPercentage: bossHealthMeterMilliPercentage));
            }

            case ObjectAction.Type.SetSpriteName:

                obj.SpriteName = action.SpriteName;

                return(new ResultOfAction(
                           newObjectAction: action,
                           shouldEndLevel: false,
                           newEnemyObjects: new List <EnemyObject>(),
                           newPowerUps: new List <Tuple <long, long> >(),
                           newSoundEffectsToPlay: new List <string>(),
                           bossHealthMeterNumber: null,
                           bossHealthMeterMilliPercentage: null));

            case ObjectAction.Type.Conditional:
                bool shouldExecute = action.Conditional.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    isParentDestroyed: isParentDestroyed,
                    isPlayerDestroyed: isPlayerDestroyed,
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                if (shouldExecute)
                {
                    var a = HandleAction(
                        action: action.ConditionalAction,
                        obj: obj,
                        playerXMillis: playerXMillis,
                        playerYMillis: playerYMillis,
                        elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                        isPlayerDestroyed: isPlayerDestroyed,
                        enemyObjectTemplates: enemyObjectTemplates,
                        rng: rng);

                    return(new ResultOfAction(
                               newObjectAction: ObjectAction.Condition(action.Conditional, a.NewObjectAction),
                               shouldEndLevel: a.ShouldEndLevel,
                               newEnemyObjects: a.NewEnemyObjects,
                               newPowerUps: a.NewPowerUps,
                               newSoundEffectsToPlay: a.NewSoundEffectsToPlay,
                               bossHealthMeterNumber: a.BossHealthMeterNumber,
                               bossHealthMeterMilliPercentage: a.BossHealthMeterMilliPercentage));
                }
                else
                {
                    return(new ResultOfAction(
                               newObjectAction: action,
                               shouldEndLevel: false,
                               newEnemyObjects: new List <EnemyObject>(),
                               newPowerUps: new List <Tuple <long, long> >(),
                               newSoundEffectsToPlay: new List <string>(),
                               bossHealthMeterNumber: null,
                               bossHealthMeterMilliPercentage: null));
                }

            case ObjectAction.Type.ConditionalNextAction:
                ResultOfAction actionResult = HandleAction(
                    action: action.ConditionalNextActionCurrentAction,
                    obj: obj,
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    isPlayerDestroyed: isPlayerDestroyed,
                    enemyObjectTemplates: enemyObjectTemplates,
                    rng: rng);

                bool shouldMoveToNext = action.ConditionalNextActionConditional.Evaluate(
                    obj.GetEnemyObjectExpressionInfo(),
                    isParentDestroyed: isParentDestroyed,
                    isPlayerDestroyed: isPlayerDestroyed,
                    playerXMillis: playerXMillis,
                    playerYMillis: playerYMillis,
                    elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                    rng: rng);

                return(new ResultOfAction(
                           newObjectAction: shouldMoveToNext
                                                        ? action.ConditionalNextActionNextAction
                                                        : ObjectAction.ConditionalNextAction(actionResult.NewObjectAction, action.ConditionalNextActionConditional, action.ConditionalNextActionNextAction),
                           shouldEndLevel: actionResult.ShouldEndLevel,
                           newEnemyObjects: actionResult.NewEnemyObjects,
                           newPowerUps: actionResult.NewPowerUps,
                           newSoundEffectsToPlay: actionResult.NewSoundEffectsToPlay,
                           bossHealthMeterNumber: actionResult.BossHealthMeterNumber,
                           bossHealthMeterMilliPercentage: actionResult.BossHealthMeterMilliPercentage));

            case ObjectAction.Type.Union:
            {
                bool shouldEndLevel                 = false;
                var  newUnionActions                = new List <ObjectAction>();
                var  enemyObjects                   = new List <EnemyObject>();
                var  newPowerUps                    = new List <Tuple <long, long> >();
                var  newSoundEffectsToPlay          = new List <string>();
                long?bossHealthMeterNumber          = null;
                long?bossHealthMeterMilliPercentage = null;

                for (var i = 0; i < action.UnionActions.Count; i++)
                {
                    ResultOfAction r = HandleAction(
                        action: action.UnionActions[i],
                        obj: obj,
                        playerXMillis: playerXMillis,
                        playerYMillis: playerYMillis,
                        elapsedMillisecondsPerIteration: elapsedMillisecondsPerIteration,
                        isPlayerDestroyed: isPlayerDestroyed,
                        enemyObjectTemplates: enemyObjectTemplates,
                        rng: rng);

                    newUnionActions.Add(r.NewObjectAction);

                    if (r.ShouldEndLevel)
                    {
                        shouldEndLevel = true;
                    }

                    foreach (EnemyObject newEnemyObj in r.NewEnemyObjects)
                    {
                        enemyObjects.Add(newEnemyObj);
                    }

                    foreach (var newPowerUp in r.NewPowerUps)
                    {
                        newPowerUps.Add(newPowerUp);
                    }

                    foreach (var newSoundEffect in r.NewSoundEffectsToPlay)
                    {
                        newSoundEffectsToPlay.Add(newSoundEffect);
                    }

                    if (r.BossHealthMeterNumber != null)
                    {
                        bossHealthMeterNumber = r.BossHealthMeterNumber;
                    }

                    if (r.BossHealthMeterMilliPercentage != null)
                    {
                        bossHealthMeterMilliPercentage = r.BossHealthMeterMilliPercentage;
                    }
                }

                return(new ResultOfAction(
                           newObjectAction: ObjectAction.Union(newUnionActions),
                           shouldEndLevel: shouldEndLevel,
                           newEnemyObjects: enemyObjects,
                           newPowerUps: newPowerUps,
                           newSoundEffectsToPlay: newSoundEffectsToPlay,
                           bossHealthMeterNumber: bossHealthMeterNumber,
                           bossHealthMeterMilliPercentage: bossHealthMeterMilliPercentage));
            }

            default:
                throw new Exception();
            }
        }