예제 #1
0
 private IEnumerable <Voxel.Coord> spreadFromCenter(Voxel.Coord center, Direction dir)
 {
     for (Voxel.Coord z = center.Move(dir, -1); z.GetComponent(dir) > center.GetComponent(dir) - 3; z = z.Move(dir.GetReverse()))
     {
         yield return(z);
     }
     for (Voxel.Coord z = center.Clone(); z.GetComponent(dir) < center.GetComponent(dir) + 3; z = z.Move(dir))
     {
         yield return(z);
     }
 }
예제 #2
0
        public void BuildFloor(Voxel floorMap, Voxel.Coord floorCoordinate, Direction forwardDir, Direction rightDir)
        {
            List <EffectBlockFactory.BlockBuildOrder> buildCoords = new List <EffectBlockFactory.BlockBuildOrder>();

            Voxel.Coord newFloorCoordinate = floorMap.GetCoordinate(this.Position);
            floorCoordinate.SetComponent(rightDir, newFloorCoordinate.GetComponent(rightDir));
            floorCoordinate.SetComponent(forwardDir, newFloorCoordinate.GetComponent(forwardDir));
            Direction upDir = floorMap.GetRelativeDirection(Direction.PositiveY);

            const int radius = 3;

            foreach (Voxel.Coord x in this.spreadFromCenter(floorCoordinate, rightDir))
            {
                if (floorMap[x.Move(upDir)].Hard)
                {
                    break;
                }
                int dx = x.GetComponent(rightDir) - floorCoordinate.GetComponent(rightDir);
                for (Voxel.Coord y = x.Move(forwardDir, -radius); y.GetComponent(forwardDir) < floorCoordinate.GetComponent(forwardDir) + radius; y = y.Move(forwardDir))
                {
                    if (floorMap[y.Move(upDir)].Hard)
                    {
                        break;
                    }
                    int dy = y.GetComponent(forwardDir) - floorCoordinate.GetComponent(forwardDir);
                    if ((float)Math.Sqrt(dx * dx + dy * dy) < radius && floorMap[y].ID == 0)
                    {
                        buildCoords.Add(new EffectBlockFactory.BlockBuildOrder
                        {
                            Voxel      = floorMap,
                            Coordinate = y,
                            State      = Voxel.States.Blue,
                        });
                    }
                }
            }
            Factory.Get <EffectBlockFactory>().Build(this.main, buildCoords, this.Position);
        }
예제 #3
0
        public void Update(float dt)
        {
            State wallRunState = this.CurrentState;

            if (wallRunState != State.None)
            {
                this.Vault.Execute();                      // Try to vault up
                if (this.CurrentState.Value == State.None) // We vaulted
                {
                    return;
                }

                if (!this.WallRunVoxel.Value.Active || this.IsSupported)
                {
                    this.Deactivate();
                    return;
                }

                Voxel voxel = this.WallRunVoxel.Value;
                if (voxel == null || !voxel.Active)
                {
                    this.Deactivate();
                    return;
                }

                Vector3 wallRunVector = voxel.GetAbsoluteVector(this.WallRunDirection.Value.GetVector());
                Vector3 baseVelocity  = voxel.LinearVelocity + Vector3.Cross(voxel.AngularVelocity, this.Position - voxel.Transform.Value.Translation);
                float   wallRunSpeed  = Vector3.Dot(this.LinearVelocity.Value - baseVelocity, wallRunVector);
                Vector3 pos           = this.Position + new Vector3(0, this.Height * -0.5f, 0);

                if (wallRunState == State.Straight)
                {
                    if (wallRunSpeed < 0.0f)
                    {
                        // Start sliding down
                        this.CurrentState.Value = wallRunState = State.Down;
                        AkSoundEngine.PostEvent(AK.EVENTS.PLAY_PLAYER_SLIDE_LOOP, this.Entity);
                    }
                }
                else if (wallRunState == State.Left || wallRunState == State.Right)
                {
                    if (this.IsSupported || wallRunSpeed < minWallRunSpeed)
                    {
                        // We landed on the ground or we're going too slow to continue wall-running
                        this.Deactivate();
                        return;
                    }
                    else
                    {
                        // Check if we should switch to another wall
                        Vector3 wallVector = voxel.GetAbsoluteVector(this.WallDirection.Value.GetVector());
                        Voxel.GlobalRaycastResult result = Voxel.GlobalRaycast(pos, wallRunVector + wallVector, 2.0f);
                        if (result.Voxel != null && result.Voxel != voxel)
                        {
                            float dot = Vector3.Dot(result.Voxel.GetAbsoluteVector(result.Normal.GetReverse().GetVector()), wallVector);
                            if (dot > 0.7f)
                            {
                                Matrix  matrix        = Matrix.CreateRotationY(this.Rotation);
                                Vector3 forwardVector = -matrix.Forward;
                                this.setup(result.Voxel, result.Normal.GetReverse(), wallRunState, forwardVector, false, true);
                            }
                        }
                    }
                }

                Voxel.Coord coord     = voxel.GetCoordinate(pos);
                Voxel.Coord wallCoord = coord.Move(this.WallDirection, 2);
                Voxel.State wallType  = voxel[wallCoord];

                if (!wallCoord.Equivalent(this.lastWallRunCoord))
                {
                    this.lastWallRunCoord = wallCoord;
                    this.WalkedOn.Execute(voxel, wallCoord, this.WallDirection);
                }

                if (this.EnableEnhancedWallRun &&
                    (wallRunState == State.Left || wallRunState == State.Right) &&
                    Zone.CanBuild(this.Position) &&
                    voxel.Entity.Type != "Bouncer")
                {
                    Direction up = voxel.GetRelativeDirection(Direction.PositiveY);
                    if (up.IsPerpendicular(this.WallDirection))
                    {
                        Direction right = this.WallDirection.Value.Cross(up);

                        List <EffectBlockFactory.BlockBuildOrder> buildCoords = new List <EffectBlockFactory.BlockBuildOrder>();

                        const int radius       = 5;
                        int       upwardRadius = wallRunState == State.Down ? 0 : radius;
                        for (Voxel.Coord x = wallCoord.Move(right, -radius); x.GetComponent(right) < wallCoord.GetComponent(right) + radius; x = x.Move(right))
                        {
                            int dx = x.GetComponent(right) - wallCoord.GetComponent(right);
                            for (Voxel.Coord y = x.Move(up, -radius); y.GetComponent(up) < wallCoord.GetComponent(up) + upwardRadius; y = y.Move(up))
                            {
                                int dy = y.GetComponent(up) - wallCoord.GetComponent(up);
                                if ((float)Math.Sqrt(dx * dx + dy * dy) < radius && voxel[y].ID == 0)
                                {
                                    buildCoords.Add(new EffectBlockFactory.BlockBuildOrder
                                    {
                                        Voxel      = voxel,
                                        Coordinate = y,
                                        State      = Voxel.States.Blue,
                                    });
                                }
                            }
                        }
                        Factory.Get <EffectBlockFactory>().Build(main, buildCoords, this.Position);
                    }
                    else
                    {
                        this.Deactivate();
                        return;
                    }
                }
                else if (wallType.ID == 0 && wallInstantiationTimer == 0.0f)                 // We ran out of wall to walk on
                {
                    this.Deactivate();
                    return;
                }

                wallInstantiationTimer = Math.Max(0.0f, wallInstantiationTimer - dt);

                Vector3 coordPos = voxel.GetAbsolutePosition(coord);

                Vector3 normal = voxel.GetAbsoluteVector(this.WallDirection.Value.GetVector());
                // Equation of a plane
                // normal (dot) point = d
                float d = Vector3.Dot(normal, coordPos) + (wallRunState == State.Down ? 0.3f : 0.4f);

                // Distance along the normal to keep the player glued to the wall
                float snapDistance = d - Vector3.Dot(pos, normal);

                this.Position.Value += normal * snapDistance;

                Vector3 velocity = this.LinearVelocity;

                // Also fix the velocity so we don't jitter away from the wall
                velocity -= Vector3.Dot(velocity, normal) * normal;

                // Slow our descent
                velocity += new Vector3(0, (wallRunState == State.Straight ? 3.0f : 10.0f) * dt, 0);

                this.LinearVelocity.Value = velocity;
            }
        }
예제 #4
0
        public static void Consolidate(Main main, DynamicVoxel voxel, Voxel targetVoxel, Voxel.Coord targetCoord, float interval = 1.0f)
        {
            if (targetVoxel != null)
            {
                // Combine this map with the other one

                Direction x = targetVoxel.GetRelativeDirection(voxel.GetAbsoluteVector(Vector3.Right));
                Direction y = targetVoxel.GetRelativeDirection(voxel.GetAbsoluteVector(Vector3.Up));
                Direction z = targetVoxel.GetRelativeDirection(voxel.GetAbsoluteVector(Vector3.Backward));

                if (x.IsParallel(y))
                {
                    x = y.Cross(z);
                }
                else if (y.IsParallel(z))
                {
                    y = x.Cross(z);
                }

                Voxel.Coord offset = new Voxel.Coord();
                float       closestCoordDistance = float.MaxValue;
                Vector3     closestCoordPosition = targetVoxel.GetAbsolutePosition(targetCoord);
                foreach (Voxel.Coord c in voxel.Chunks.SelectMany(c => c.Boxes).SelectMany(b => b.GetCoords()))
                {
                    float distance = (voxel.GetAbsolutePosition(c) - closestCoordPosition).LengthSquared();
                    if (distance < closestCoordDistance)
                    {
                        closestCoordDistance = distance;
                        offset = c;
                    }
                }
                Vector3 toLevitatingMap = voxel.Transform.Value.Translation - targetVoxel.GetAbsolutePosition(targetCoord);
                offset = offset.Move(voxel.GetRelativeDirection(-toLevitatingMap));

                Quaternion orientation = Quaternion.CreateFromRotationMatrix(voxel.Transform.Value);

                EffectBlockFactory blockFactory = Factory.Get <EffectBlockFactory>();

                int index = 0;
                List <Voxel.Coord> coords = voxel.Chunks.SelectMany(c => c.Boxes).SelectMany(b => b.GetCoords()).ToList();
                Voxel.Coord        camera = voxel.GetCoordinate(main.Camera.Position);
                foreach (Voxel.Coord c in coords.OrderBy(c2 => new Vector3(c2.X - camera.X, c2.Y - camera.Y, c2.Z - camera.Z).LengthSquared()))
                {
                    Voxel.Coord offsetFromCenter = c.Move(-offset.X, -offset.Y, -offset.Z);
                    Voxel.Coord targetCoord2     = new Voxel.Coord();
                    targetCoord2.SetComponent(x, offsetFromCenter.GetComponent(Direction.PositiveX));
                    targetCoord2.SetComponent(y, offsetFromCenter.GetComponent(Direction.PositiveY));
                    targetCoord2.SetComponent(z, offsetFromCenter.GetComponent(Direction.PositiveZ));
                    targetCoord2 = targetCoord2.Move(targetCoord.X, targetCoord.Y, targetCoord.Z);
                    if (targetVoxel[targetCoord2].ID == 0)
                    {
                        Entity blockEntity = blockFactory.CreateAndBind(main);
                        c.Data.ApplyToEffectBlock(blockEntity.Get <ModelInstance>());
                        EffectBlock effectBlock = blockEntity.Get <EffectBlock>();
                        effectBlock.Offset.Value     = targetVoxel.GetRelativePosition(targetCoord2);
                        effectBlock.DoScale          = false;
                        effectBlock.StartPosition    = voxel.GetAbsolutePosition(c);
                        effectBlock.StartOrientation = orientation;
                        effectBlock.TotalLifetime    = (0.05f + (index * 0.0075f)) * interval;
                        effectBlock.Setup(targetVoxel.Entity, targetCoord2, c.Data.ID);
                        main.Add(blockEntity);
                        index++;
                    }
                }

                // Delete the map
                voxel.Entity.Delete.Execute();
            }
        }
예제 #5
0
        public bool BreakWalls(Vector3 forward, Vector3 right)
        {
            BlockFactory blockFactory = Factory.Get <BlockFactory>();
            Vector3      basePos      = this.Position + new Vector3(0, 0.2f + (this.Height * -0.5f) - this.SupportHeight, 0) + forward * -1.0f;
            bool         broke        = false;

            foreach (Voxel map in Voxel.ActivePhysicsVoxels.ToList())
            {
                List <Voxel.Coord> removals      = new List <Voxel.Coord>();
                Quaternion         mapQuaternion = Quaternion.CreateFromRotationMatrix(map.Transform);
                Voxel.Coord        top           = map.GetCoordinate(basePos + new Vector3(0, this.Height + this.SupportHeight + 0.5f, 0));
                Direction          upDir         = map.GetRelativeDirection(Vector3.Up);
                Direction          rightDir      = map.GetRelativeDirection(right);
                Direction          forwardDir    = map.GetRelativeDirection(forward);
                Voxel.Coord        center        = map.GetCoordinate(basePos);
                for (Voxel.Coord y = center.Clone(); y.GetComponent(upDir) <= top.GetComponent(upDir); y = y.Move(upDir))
                {
                    int minZ = center.GetComponent(rightDir) - 10;
                    int maxZ = minZ + 20;
                    foreach (Voxel.Coord x in this.spreadFromCenter(y, rightDir))
                    {
                        Voxel.Coord z = x.Clone();
                        for (int i = 0; i < 4; i++)
                        {
                            Voxel.State state           = map[z];
                            int         zRightDimension = z.GetComponent(rightDir);
                            if (zRightDimension > minZ && zRightDimension < maxZ && state.ID != 0 && !removals.Contains(z))
                            {
                                if (state.Permanent || state.Hard)
                                {
                                    if (zRightDimension >= center.GetComponent(rightDir))
                                    {
                                        maxZ = zRightDimension;
                                    }
                                    else
                                    {
                                        minZ = zRightDimension;
                                    }
                                    break;
                                }
                                else
                                {
                                    broke = true;
                                    removals.Add(z);
                                    Vector3   cellPos        = map.GetAbsolutePosition(z);
                                    Vector3   toCell         = cellPos - basePos;
                                    Entity    block          = blockFactory.CreateAndBind(this.main);
                                    Transform blockTransform = block.Get <Transform>();
                                    blockTransform.Position.Value   = cellPos;
                                    blockTransform.Quaternion.Value = mapQuaternion;
                                    state.ApplyToBlock(block);
                                    toCell += forward * 4.0f;
                                    toCell.Normalize();
                                    PhysicsBlock physicsBlock = block.Get <PhysicsBlock>();
                                    physicsBlock.LinearVelocity.Value  = toCell * 15.0f;
                                    physicsBlock.AngularVelocity.Value = new Vector3(((float)this.random.NextDouble() - 0.5f) * 2.0f, ((float)this.random.NextDouble() - 0.5f) * 2.0f, ((float)this.random.NextDouble() - 0.5f) * 2.0f);
                                    main.Add(block);
                                }
                            }
                            z = z.Move(forwardDir);
                        }
                    }
                }

                if (removals.Count > 0)
                {
                    map.Empty(removals);
                    map.Regenerate();
                }
            }
            return(broke);
        }
예제 #6
0
        public override void Bind(Entity entity, Main main, bool creating = false)
        {
            PointLight light = entity.GetOrCreate <PointLight>("PointLight");

            light.Serialize = false;

            const float defaultLightAttenuation = 15.0f;

            light.Attenuation.Value = defaultLightAttenuation;

            Transform transform = entity.GetOrCreate <Transform>("Transform");

            light.Add(new Binding <Vector3>(light.Position, transform.Position));

            if (!main.EditorEnabled)
            {
                Sound.AttachTracker(entity);
                SoundKiller.Add(entity, AK.EVENTS.STOP_GLOWSQUARE);
                entity.Add(new PostInitialization(delegate()
                {
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_GLOWSQUARE, entity);
                    AkSoundEngine.SetRTPCValue(AK.GAME_PARAMETERS.SFX_GLOWSQUARE_PITCH, -1.0f, entity);
                }));
            }

            AI ai = entity.GetOrCreate <AI>("AI");

            ModelAlpha model = entity.GetOrCreate <ModelAlpha>();

            model.Add(new Binding <Matrix>(model.Transform, transform.Matrix));
            model.Filename.Value  = "AlphaModels\\box";
            model.Serialize       = false;
            model.DrawOrder.Value = 15;

            const float defaultModelScale = 1.0f;

            model.Scale.Value = new Vector3(defaultModelScale);

            model.Add(new Binding <Vector3, string>(model.Color, delegate(string state)
            {
                switch (state)
                {
                case "Alert":
                    return(new Vector3(1.5f, 1.5f, 0.5f));

                case "Chase":
                    return(new Vector3(1.5f, 0.5f, 0.5f));

                case "Explode":
                    return(new Vector3(2.0f, 1.0f, 0.5f));

                default:
                    return(new Vector3(1.0f, 1.0f, 1.0f));
                }
            }, ai.CurrentState));

            entity.Add(new Updater
                       (
                           delegate(float dt)
            {
                float source            = 1.0f + ((float)this.random.NextDouble() - 0.5f) * 2.0f * 0.05f;
                model.Scale.Value       = new Vector3(defaultModelScale * source);
                light.Attenuation.Value = defaultLightAttenuation * source;
            }
                       ));

            model.Add(new Binding <bool, string>(model.Enabled, x => x != "Exploding", ai.CurrentState));

            light.Add(new Binding <Vector3>(light.Color, model.Color));

            Agent agent = entity.GetOrCreate <Agent>();

            agent.Add(new Binding <Vector3>(agent.Position, transform.Position));

            RaycastAIMovement movement = entity.GetOrCreate <RaycastAIMovement>("Movement");
            Exploder          exploder = entity.GetOrCreate <Exploder>("Exploder");

            AI.Task checkOperationalRadius = new AI.Task
            {
                Interval = 2.0f,
                Action   = delegate()
                {
                    bool shouldBeActive = (transform.Position.Value - main.Camera.Position).Length() < movement.OperationalRadius;
                    if (shouldBeActive && ai.CurrentState == "Suspended")
                    {
                        ai.CurrentState.Value = "Idle";
                    }
                    else if (!shouldBeActive && ai.CurrentState != "Suspended")
                    {
                        ai.CurrentState.Value = "Suspended";
                    }
                },
            };

            RaycastAI raycastAI = entity.GetOrCreate <RaycastAI>("RaycastAI");

            raycastAI.Add(new TwoWayBinding <Vector3>(transform.Position, raycastAI.Position));
            raycastAI.Add(new Binding <Quaternion>(transform.Quaternion, raycastAI.Orientation));

            AI.Task updatePosition = new AI.Task
            {
                Action = delegate()
                {
                    raycastAI.Update();
                },
            };

            ai.Add(new AI.AIState
            {
                Name  = "Suspended",
                Tasks = new[] { checkOperationalRadius, },
            });

            const float sightDistance   = 40.0f;
            const float hearingDistance = 0.0f;

            ai.Add(new AI.AIState
            {
                Name  = "Idle",
                Enter = delegate(AI.AIState previous)
                {
                    AkSoundEngine.SetRTPCValue(AK.GAME_PARAMETERS.SFX_GLOWSQUARE_PITCH, -1.0f, entity);
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    updatePosition,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action   = delegate()
                        {
                            raycastAI.Move(new Vector3(((float)this.random.NextDouble() * 2.0f) - 1.0f, ((float)this.random.NextDouble() * 2.0f) - 1.0f, ((float)this.random.NextDouble() * 2.0f) - 1.0f));
                        }
                    },
                    new AI.Task
                    {
                        Interval = 0.5f,
                        Action   = delegate()
                        {
                            Agent a = Agent.Query(transform.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                            if (a != null)
                            {
                                ai.CurrentState.Value = "Alert";
                            }
                        },
                    },
                },
            });

            ai.Add(new AI.AIState
            {
                Name  = "Alert",
                Enter = delegate(AI.AIState previous)
                {
                    AkSoundEngine.PostEvent(AK.EVENTS.STOP_GLOWSQUARE, entity);
                },
                Exit = delegate(AI.AIState next)
                {
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_GLOWSQUARE, entity);
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    updatePosition,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action   = delegate()
                        {
                            if (ai.TimeInCurrentState > 3.0f)
                            {
                                ai.CurrentState.Value = "Idle";
                            }
                            else
                            {
                                Agent a = Agent.Query(transform.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                                if (a != null)
                                {
                                    ai.TargetAgent.Value  = a.Entity;
                                    ai.CurrentState.Value = "Chase";
                                }
                            }
                        },
                    },
                },
            });

            AI.Task checkTargetAgent = new AI.Task
            {
                Action = delegate()
                {
                    Entity target = ai.TargetAgent.Value.Target;
                    if (target == null || !target.Active)
                    {
                        ai.TargetAgent.Value  = null;
                        ai.CurrentState.Value = "Idle";
                    }
                },
            };

            ai.Add(new AI.AIState
            {
                Name  = "Chase",
                Enter = delegate(AI.AIState previous)
                {
                    AkSoundEngine.SetRTPCValue(AK.GAME_PARAMETERS.SFX_GLOWSQUARE_PITCH, 0.0f, entity);
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    checkTargetAgent,
                    new AI.Task
                    {
                        Interval = 0.35f,
                        Action   = delegate()
                        {
                            raycastAI.Move(ai.TargetAgent.Value.Target.Get <Transform>().Position.Value - transform.Position);
                        }
                    },
                    new AI.Task
                    {
                        Action = delegate()
                        {
                            if ((ai.TargetAgent.Value.Target.Get <Transform>().Position.Value - transform.Position).Length() < 10.0f)
                            {
                                ai.CurrentState.Value = "Explode";
                            }
                        }
                    },
                    updatePosition,
                },
            });

            EffectBlockFactory factory = Factory.Get <EffectBlockFactory>();

            ai.Add(new AI.AIState
            {
                Name  = "Explode",
                Enter = delegate(AI.AIState previous)
                {
                    exploder.CoordQueue.Clear();

                    Entity voxelEntity = raycastAI.Voxel.Value.Target;
                    if (voxelEntity == null || !voxelEntity.Active)
                    {
                        ai.CurrentState.Value = "Alert";
                        return;
                    }

                    Voxel m = voxelEntity.Get <Voxel>();

                    Voxel.Coord c = raycastAI.Coord.Value;

                    Direction toSupport = Direction.None;

                    foreach (Direction dir in DirectionExtensions.Directions)
                    {
                        if (m[raycastAI.Coord.Value.Move(dir)].ID != 0)
                        {
                            toSupport = dir;
                            break;
                        }
                    }

                    if (toSupport == Direction.None)
                    {
                        ai.CurrentState.Value = "Alert";
                        return;
                    }

                    Direction up = toSupport.GetReverse();

                    exploder.ExplosionOriginalCoord.Value = raycastAI.Coord;

                    Direction right;
                    if (up.IsParallel(Direction.PositiveX))
                    {
                        right = Direction.PositiveZ;
                    }
                    else
                    {
                        right = Direction.PositiveX;
                    }
                    Direction forward = up.Cross(right);

                    for (Voxel.Coord y = c.Clone(); y.GetComponent(up) < c.GetComponent(up) + 3; y = y.Move(up))
                    {
                        for (Voxel.Coord x = y.Clone(); x.GetComponent(right) < c.GetComponent(right) + 2; x = x.Move(right))
                        {
                            for (Voxel.Coord z = x.Clone(); z.GetComponent(forward) < c.GetComponent(forward) + 2; z = z.Move(forward))
                            {
                                exploder.CoordQueue.Add(z);
                            }
                        }
                    }
                },
                Exit = delegate(AI.AIState next)
                {
                    exploder.CoordQueue.Clear();
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Interval = 0.15f,
                        Action   = delegate()
                        {
                            if (exploder.CoordQueue.Length > 0)
                            {
                                raycastAI.MoveTo(exploder.CoordQueue[0]);

                                exploder.CoordQueue.RemoveAt(0);

                                Entity blockEntity = factory.CreateAndBind(main);
                                Voxel.States.Infected.ApplyToEffectBlock(blockEntity.Get <ModelInstance>());

                                Entity mapEntity = raycastAI.Voxel.Value.Target;
                                if (mapEntity != null && mapEntity.Active)
                                {
                                    EffectBlock effectBlock = blockEntity.Get <EffectBlock>();
                                    Voxel m = mapEntity.Get <Voxel>();

                                    effectBlock.Offset.Value = m.GetRelativePosition(raycastAI.Coord);

                                    Vector3 absolutePos = m.GetAbsolutePosition(raycastAI.Coord);

                                    effectBlock.StartPosition    = absolutePos + new Vector3(0.05f, 0.1f, 0.05f);
                                    effectBlock.StartOrientation = Quaternion.CreateFromYawPitchRoll(0.15f, 0.15f, 0);
                                    effectBlock.TotalLifetime    = 0.05f;
                                    effectBlock.Setup(raycastAI.Voxel.Value.Target, raycastAI.Coord, Voxel.t.Infected);
                                    main.Add(blockEntity);
                                }
                            }
                        }
                    },
                    new AI.Task
                    {
                        Action = delegate()
                        {
                            AkSoundEngine.SetRTPCValue(AK.GAME_PARAMETERS.SFX_GLOWSQUARE_PITCH, MathHelper.Lerp(0.0f, 1.0f, ai.TimeInCurrentState.Value / 2.0f), entity);
                            if (exploder.CoordQueue.Length == 0)
                            {
                                // Explode
                                ai.CurrentState.Value = "Exploding";
                            }
                        },
                    },
                    updatePosition,
                },
            });

            ai.Add(new AI.AIState
            {
                Name  = "Exploding",
                Enter = delegate(AI.AIState previous)
                {
                    exploder.Exploded.Value = false;
                    AkSoundEngine.PostEvent(AK.EVENTS.STOP_GLOWSQUARE, entity);
                },
                Exit = delegate(AI.AIState next)
                {
                    exploder.Exploded.Value = false;
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_GLOWSQUARE, entity);
                },
                Tasks = new[]
                {
                    new AI.Task
                    {
                        Interval = 0.1f,
                        Action   = delegate()
                        {
                            const int radius = 9;

                            float timeInCurrentState = ai.TimeInCurrentState;
                            if (timeInCurrentState > 1.0f && !exploder.Exploded)
                            {
                                Entity mapEntity = raycastAI.Voxel.Value.Target;
                                if (mapEntity != null && mapEntity.Active)
                                {
                                    Explosion.Explode(main, mapEntity.Get <Voxel>(), raycastAI.Coord, radius, 18.0f);
                                }

                                exploder.Exploded.Value = true;
                            }

                            if (timeInCurrentState > 2.0f)
                            {
                                raycastAI.Move(new Vector3(((float)this.random.NextDouble() * 2.0f) - 1.0f, ((float)this.random.NextDouble() * 2.0f) - 1.0f, ((float)this.random.NextDouble() * 2.0f) - 1.0f));
                                ai.CurrentState.Value = "Alert";
                            }
                        },
                    },
                    updatePosition,
                },
            });

            this.SetMain(entity, main);

            entity.Add("OperationalRadius", movement.OperationalRadius);
        }