Example #1
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            PointLight light = result.GetOrCreate<PointLight>("PointLight");
            light.Serialize = false;

            const float defaultLightAttenuation = 15.0f;
            light.Attenuation.Value = defaultLightAttenuation;

            Transform transform = result.GetOrCreate<Transform>("Transform");
            light.Add(new Binding<Vector3>(light.Position, transform.Position));

            VoxelChaseAI chase = result.GetOrCreate<VoxelChaseAI>("VoxelChaseAI");

            chase.Filter = delegate(Map.CellState state)
            {
                return state.ID == 0 ? VoxelChaseAI.Cell.Empty : VoxelChaseAI.Cell.Filled;
            };

            chase.Add(new TwoWayBinding<Vector3>(transform.Position, chase.Position));
            result.Add(new CommandBinding(chase.Delete, result.Delete));

            Sound sound = result.GetOrCreate<Sound>("LoopSound");
            sound.Serialize = false;
            sound.Cue.Value = "Orb Loop";
            sound.Is3D.Value = true;
            sound.IsPlaying.Value = true;
            sound.Add(new Binding<Vector3>(sound.Position, chase.Position));
            Property<float> volume = sound.GetProperty("Volume");
            Property<float> pitch = sound.GetProperty("Pitch");

            const float defaultVolume = 0.5f;
            volume.Value = defaultVolume;

            AI ai = result.GetOrCreate<AI>();

            Model model = result.GetOrCreate<Model>();
            model.Add(new Binding<Matrix>(model.Transform, transform.Matrix));
            model.Filename.Value = "Models\\sphere";
            model.Editable = false;
            model.Serialize = false;

            const float defaultModelScale = 0.25f;
            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 "Levitating":
                        return new Vector3(2.0f, 1.0f, 0.5f);
                    case "Idle":
                        return new Vector3(1.0f, 1.0f, 1.0f);
                    default:
                        return new Vector3(0.0f, 0.0f, 0.0f);
                }
            }, ai.CurrentState));

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

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

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

            Agent agent = result.GetOrCreate<Agent>();
            agent.Add(new Binding<Vector3>(agent.Position, chase.Position));

            Property<int> operationalRadius = result.GetOrMakeProperty<int>("OperationalRadius", true, 100);

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

            const float sightDistance = 30.0f;
            const float hearingDistance = 15.0f;

            ai.Add(new AI.State
            {
                Name = "Idle",
                Enter = delegate(AI.State previous)
                {
                    chase.Speed.Value = 3.0f;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action = delegate()
                        {
                            Agent a = Agent.Query(chase.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                            if (a != null)
                                ai.CurrentState.Value = "Alert";
                        },
                    },
                },
            });

            Property<Entity.Handle> targetAgent = result.GetOrMakeProperty<Entity.Handle>("TargetAgent");

            ai.Add(new AI.State
            {
                Name = "Alert",
                Enter = delegate(AI.State previous)
                {
                    chase.Enabled.Value = false;
                },
                Exit = delegate(AI.State next)
                {
                    chase.Enabled.Value = true;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action = delegate()
                        {
                            if (ai.TimeInCurrentState > 3.0f)
                                ai.CurrentState.Value = "Idle";
                            else
                            {
                                Agent a = Agent.Query(chase.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                                if (a != null)
                                {
                                    targetAgent.Value = a.Entity;
                                    ai.CurrentState.Value = "Chase";
                                }
                            }
                        },
                    },
                },
            });

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

            // Levitate

            Property<Entity.Handle> levitatingMap = result.GetOrMakeProperty<Entity.Handle>("LevitatingMap");
            Property<Map.Coordinate> grabCoord = result.GetOrMakeProperty<Map.Coordinate>("GrabCoord");

            const int levitateRipRadius = 4;

            Func<bool> tryLevitate = delegate()
            {
                Map map = chase.Map.Value.Target.Get<Map>();
                Map.Coordinate? candidate = map.FindClosestFilledCell(chase.Coord, 3);

                if (!candidate.HasValue)
                    return false;

                Map.Coordinate center = candidate.Value;
                if (!map[center].Permanent)
                {
                    // Break off a chunk of this map into a new DynamicMap.

                    List<Map.Coordinate> edges = new List<Map.Coordinate>();

                    Map.Coordinate ripStart = center.Move(-levitateRipRadius, -levitateRipRadius, -levitateRipRadius);
                    Map.Coordinate ripEnd = center.Move(levitateRipRadius, levitateRipRadius, levitateRipRadius);

                    Dictionary<Map.Box, bool> permanentBoxes = new Dictionary<Map.Box, bool>();
                    foreach (Map.Coordinate c in ripStart.CoordinatesBetween(ripEnd))
                    {
                        Map.Box box = map.GetBox(c);
                        if (box != null && box.Type.Permanent)
                            permanentBoxes[box] = true;
                    }

                    foreach (Map.Box b in permanentBoxes.Keys)
                    {
                        // Top and bottom
                        for (int x = b.X - 1; x <= b.X + b.Width; x++)
                        {
                            for (int z = b.Z - 1; z <= b.Z + b.Depth; z++)
                            {
                                Map.Coordinate coord = new Map.Coordinate { X = x, Y = b.Y + b.Height, Z = z };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);

                                coord = new Map.Coordinate { X = x, Y = b.Y - 1, Z = z };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);
                            }
                        }

                        // Outer shell
                        for (int y = b.Y; y < b.Y + b.Height; y++)
                        {
                            // Left and right
                            for (int z = b.Z - 1; z <= b.Z + b.Depth; z++)
                            {
                                Map.Coordinate coord = new Map.Coordinate { X = b.X - 1, Y = y, Z = z };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);

                                coord = new Map.Coordinate { X = b.X + b.Width, Y = y, Z = z };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);
                            }

                            // Backward and forward
                            for (int x = b.X; x < b.X + b.Width; x++)
                            {
                                Map.Coordinate coord = new Map.Coordinate { X = x, Y = y, Z = b.Z - 1 };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);

                                coord = new Map.Coordinate { X = x, Y = y, Z = b.Z + b.Depth };
                                if (coord.Between(ripStart, ripEnd))
                                    edges.Add(coord);
                            }
                        }
                    }

                    if (edges.Contains(center))
                        return false;

                    // Top and bottom
                    for (int x = ripStart.X; x <= ripEnd.X; x++)
                    {
                        for (int z = ripStart.Z; z <= ripEnd.Z; z++)
                        {
                            edges.Add(new Map.Coordinate { X = x, Y = ripStart.Y, Z = z });
                            edges.Add(new Map.Coordinate { X = x, Y = ripEnd.Y, Z = z });
                        }
                    }

                    // Sides
                    for (int y = ripStart.Y + 1; y <= ripEnd.Y - 1; y++)
                    {
                        // Left and right
                        for (int z = ripStart.Z; z <= ripEnd.Z; z++)
                        {
                            edges.Add(new Map.Coordinate { X = ripStart.X, Y = y, Z = z });
                            edges.Add(new Map.Coordinate { X = ripEnd.X, Y = y, Z = z });
                        }

                        // Backward and forward
                        for (int x = ripStart.X; x <= ripEnd.X; x++)
                        {
                            edges.Add(new Map.Coordinate { X = x, Y = y, Z = ripStart.Z });
                            edges.Add(new Map.Coordinate { X = x, Y = y, Z = ripEnd.Z });
                        }
                    }

                    map.Empty(edges);
                    map.Regenerate(delegate(List<DynamicMap> spawnedMaps)
                    {
                        foreach (DynamicMap spawnedMap in spawnedMaps)
                        {
                            if (spawnedMap[center].ID != 0)
                            {
                                levitatingMap.Value = spawnedMap.Entity;
                                break;
                            }
                        }
                    });

                    grabCoord.Value = center;
                    return true;
                }
                return false;
            };

            Action delevitateMap = delegate()
            {
                Entity levitatingMapEntity = levitatingMap.Value.Target;
                if (levitatingMapEntity == null || !levitatingMapEntity.Active)
                    return;

                DynamicMap dynamicMap = levitatingMapEntity.Get<DynamicMap>();

                int maxDistance = levitateRipRadius + 7;
                Map closestMap = null;
                Map.Coordinate closestCoord = new Map.Coordinate();
                foreach (Map m in Map.ActivePhysicsMaps)
                {
                    if (m == dynamicMap)
                        continue;

                    Map.Coordinate relativeCoord = m.GetCoordinate(dynamicMap.Transform.Value.Translation);
                    Map.Coordinate? closestFilled = m.FindClosestFilledCell(relativeCoord, maxDistance);
                    if (closestFilled != null)
                    {
                        maxDistance = Math.Min(Math.Abs(relativeCoord.X - closestFilled.Value.X), Math.Min(Math.Abs(relativeCoord.Y - closestFilled.Value.Y), Math.Abs(relativeCoord.Z - closestFilled.Value.Z)));
                        closestMap = m;
                        closestCoord = closestFilled.Value;
                    }
                }
                if (closestMap != null)
                {
                    // Combine this map with the other one

                    Direction x = closestMap.GetRelativeDirection(dynamicMap.GetAbsoluteVector(Vector3.Right));
                    Direction y = closestMap.GetRelativeDirection(dynamicMap.GetAbsoluteVector(Vector3.Up));
                    Direction z = closestMap.GetRelativeDirection(dynamicMap.GetAbsoluteVector(Vector3.Backward));

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

                    Map.Coordinate offset = new Map.Coordinate();
                    float closestCoordDistance = float.MaxValue;
                    Vector3 closestCoordPosition = closestMap.GetAbsolutePosition(closestCoord);
                    foreach (Map.Coordinate c in dynamicMap.Chunks.SelectMany(c => c.Boxes).SelectMany(b => b.GetCoords()))
                    {
                        float distance = (dynamicMap.GetAbsolutePosition(c) - closestCoordPosition).LengthSquared();
                        if (distance < closestCoordDistance)
                        {
                            closestCoordDistance = distance;
                            offset = c;
                        }
                    }
                    Vector3 toLevitatingMap = dynamicMap.Transform.Value.Translation - closestMap.GetAbsolutePosition(closestCoord);
                    offset = offset.Move(dynamicMap.GetRelativeDirection(-toLevitatingMap));

                    Matrix orientation = dynamicMap.Transform.Value;
                    orientation.Translation = Vector3.Zero;

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

                    int index = 0;
                    foreach (Map.Coordinate c in dynamicMap.Chunks.SelectMany(c => c.Boxes).SelectMany(b => b.GetCoords()).OrderBy(c2 => new Vector3(c2.X - offset.X, c2.Y - offset.Y, c2.Z - offset.Z).LengthSquared()))
                    {
                        Map.Coordinate offsetFromCenter = c.Move(-offset.X, -offset.Y, -offset.Z);
                        Map.Coordinate targetCoord = new Map.Coordinate();
                        targetCoord.SetComponent(x, offsetFromCenter.GetComponent(Direction.PositiveX));
                        targetCoord.SetComponent(y, offsetFromCenter.GetComponent(Direction.PositiveY));
                        targetCoord.SetComponent(z, offsetFromCenter.GetComponent(Direction.PositiveZ));
                        targetCoord = targetCoord.Move(closestCoord.X, closestCoord.Y, closestCoord.Z);
                        if (closestMap[targetCoord].ID == 0)
                        {
                            Entity block = blockFactory.CreateAndBind(main);
                            c.Data.ApplyToEffectBlock(block.Get<ModelInstance>());
                            block.GetProperty<Vector3>("Offset").Value = closestMap.GetRelativePosition(targetCoord);
                            block.GetProperty<bool>("Scale").Value = false;
                            block.GetProperty<Vector3>("StartPosition").Value = dynamicMap.GetAbsolutePosition(c);
                            block.GetProperty<Matrix>("StartOrientation").Value = orientation;
                            block.GetProperty<float>("TotalLifetime").Value = 0.05f + (index * 0.0075f);
                            blockFactory.Setup(block, closestMap.Entity, targetCoord, c.Data.ID);
                            main.Add(block);
                            index++;
                        }
                    }

                    // Delete the map
                    levitatingMapEntity.Delete.Execute();
                }
            };

            // Chase AI state

            ai.Add(new AI.State
            {
                Name = "Chase",
                Enter = delegate(AI.State previous)
                {
                    chase.Speed.Value = 10.0f;
                    chase.TargetActive.Value = true;
                },
                Exit = delegate(AI.State next)
                {
                    chase.TargetActive.Value = false;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    checkTargetAgent,
                    new AI.Task
                    {
                        Interval = 0.1f,
                        Action = delegate()
                        {
                            Entity target = targetAgent.Value.Target;
                            Vector3 targetPosition = target.Get<Transform>().Position;
                            chase.Target.Value = targetPosition;
                            Entity levitatingMapEntity = levitatingMap.Value.Target;
                            if ((targetPosition - chase.Position).Length() < 10.0f && (levitatingMapEntity == null || !levitatingMapEntity.Active))
                            {
                                if (tryLevitate())
                                    ai.CurrentState.Value = "Levitating";
                            }
                        }
                    }
                },
            });

            Property<Vector3> lastPosition = result.GetOrMakeProperty<Vector3>("LastPosition");
            Property<Vector3> nextPosition = result.GetOrMakeProperty<Vector3>("NextPosition");
            Property<float> positionBlend = result.GetOrMakeProperty<float>("PositionBlend");

            Action findNextPosition = delegate()
            {
                lastPosition.Value = chase.Position.Value;
                nextPosition.Value = targetAgent.Value.Target.Get<Transform>().Position + new Vector3((float)random.NextDouble() - 0.5f, (float)random.NextDouble(), (float)random.NextDouble() - 0.5f) * 5.0f;
                positionBlend.Value = 0.0f;
            };

            ai.Add(new AI.State
            {
                Name = "Levitating",
                Enter = delegate(AI.State previous)
                {
                    chase.Enabled.Value = false;
                    findNextPosition();
                },
                Exit = delegate(AI.State next)
                {
                    delevitateMap();
                    levitatingMap.Value = null;

                    Map map = chase.Map.Value.Target.Get<Map>();
                    Map.Coordinate currentCoord = map.GetCoordinate(chase.Position);
                    Map.Coordinate? closest = map.FindClosestFilledCell(currentCoord, 10);
                    if (closest.HasValue)
                    {
                        chase.LastCoord.Value = currentCoord;
                        chase.Coord.Value = closest.Value;
                        chase.Blend.Value = 0.0f;
                    }
                    chase.Enabled.Value = true;
                    volume.Value = defaultVolume;
                    pitch.Value = 0.0f;
                },
                Tasks = new[]
                {
                    checkTargetAgent,
                    new AI.Task
                    {
                        Action = delegate()
                        {
                            volume.Value = 1.0f;
                            pitch.Value = 1.0f;
                            Entity levitatingMapEntity = levitatingMap.Value.Target;
                            if (!levitatingMapEntity.Active || ai.TimeInCurrentState.Value > 8.0f)
                            {
                                ai.CurrentState.Value = "Alert";
                                return;
                            }

                            DynamicMap dynamicMap = levitatingMapEntity.Get<DynamicMap>();

                            positionBlend.Value += (main.ElapsedTime.Value / 1.0f);
                            if (positionBlend > 1.0f)
                                findNextPosition();

                            chase.Position.Value = Vector3.Lerp(lastPosition, nextPosition, positionBlend);

                            Vector3 grabPoint = dynamicMap.GetAbsolutePosition(grabCoord);
                            Vector3 diff = chase.Position.Value - grabPoint;
                            if (diff.Length() > 15.0f)
                            {
                                ai.CurrentState.Value = "Chase";
                                return;
                            }

                            diff *= (float)Math.Sqrt(dynamicMap.PhysicsEntity.Mass) * 0.5f;
                            dynamicMap.PhysicsEntity.ApplyImpulse(ref grabPoint, ref diff);
                        },
                    },
                },
            });

            this.SetMain(result, main);
        }
Example #2
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            if (ParticleSystem.Get(main, "SnakeSparks") == null)
            {
                ParticleSystem.Add(main, "SnakeSparks",
                new ParticleSystem.ParticleSettings
                {
                    TextureName = "Particles\\splash",
                    MaxParticles = 1000,
                    Duration = TimeSpan.FromSeconds(1.0f),
                    MinHorizontalVelocity = -7.0f,
                    MaxHorizontalVelocity = 7.0f,
                    MinVerticalVelocity = 0.0f,
                    MaxVerticalVelocity = 7.0f,
                    Gravity = new Vector3(0.0f, -10.0f, 0.0f),
                    MinRotateSpeed = -2.0f,
                    MaxRotateSpeed = 2.0f,
                    MinStartSize = 0.3f,
                    MaxStartSize = 0.7f,
                    MinEndSize = 0.0f,
                    MaxEndSize = 0.0f,
                    BlendState = Microsoft.Xna.Framework.Graphics.BlendState.AlphaBlend,
                    MinColor = new Vector4(2.0f, 2.0f, 2.0f, 1.0f),
                    MaxColor = new Vector4(2.0f, 2.0f, 2.0f, 1.0f),
                });
            }

            result.CannotSuspendByDistance = true;
            Transform transform = result.Get<Transform>();

            PointLight light = result.GetOrCreate<PointLight>("Light");
            light.Color.Value = new Vector3(1.3f, 0.5f, 0.5f);
            light.Attenuation.Value = 10.0f;
            light.Shadowed.Value = false;
            light.Serialize = false;

            EnemyBase enemy = result.GetOrCreate<EnemyBase>("Base");

            enemy.Add(new Binding<Matrix>(enemy.Transform, transform.Matrix));
            enemy.Add(new CommandBinding(enemy.Delete, result.Delete));

            Property<float> operationalRadius = result.GetOrMakeProperty<float>("OperationalRadius", true, 100.0f);

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

            ListProperty<Map.Coordinate> path = result.GetOrMakeListProperty<Map.Coordinate>("PathCoordinates");

            Property<Entity.Handle> targetAgent = result.GetOrMakeProperty<Entity.Handle>("TargetAgent");

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

            Agent agent = result.GetOrCreate<Agent>("Agent");

            Map.CellState fillState = WorldFactory.StatesByName["Snake"];
            Map.CellState criticalState = WorldFactory.StatesByName["InfectedCritical"];
            Map.CellState temporaryState = WorldFactory.StatesByName["Temporary"];

            VoxelChaseAI chase = null;
            result.Add(new PostInitialization
            {
                delegate()
                {
                    if (chase.Map.Value.Target == null)
                        chase.Position.Value = enemy.Position;
                }
            });
            chase = result.GetOrCreate<VoxelChaseAI>("VoxelChaseAI");
            chase.Filter = delegate(Map.CellState state)
            {
                int id = state.ID;
                if (id == fillState.ID || id == temporaryState.ID || id == 0)
                    return VoxelChaseAI.Cell.Empty;
                if (state.Permanent || id == criticalState.ID)
                    return VoxelChaseAI.Cell.Filled;
                return VoxelChaseAI.Cell.Penetrable;
            };
            result.Add(new CommandBinding(chase.Delete, result.Delete));

            PointLight positionLight = null;
            Property<float> positionLightRadius = result.GetOrMakeProperty<float>("PositionLightRadius", true, 20.0f);
            if (!main.EditorEnabled)
            {
                positionLight = new PointLight();
                positionLight.Serialize = false;
                positionLight.Color.Value = new Vector3(1.5f, 0.5f, 0.5f);
                positionLight.Add(new Binding<float>(positionLight.Attenuation, positionLightRadius));
                positionLight.Shadowed.Value = false;
                positionLight.Add(new Binding<bool, string>(positionLight.Enabled, x => x != "Suspended", ai.CurrentState));
                positionLight.Add(new Binding<Vector3, string>(positionLight.Color, delegate(string state)
                {
                    switch (state)
                    {
                        case "Chase":
                        case "Crush":
                            return new Vector3(1.5f, 0.5f, 0.5f);
                        case "Alert":
                            return new Vector3(1.5f, 1.5f, 0.5f);
                        default:
                            return new Vector3(1.0f, 1.0f, 1.0f);
                    }
                }, ai.CurrentState));
                result.Add("PositionLight", positionLight);
                ParticleEmitter emitter = result.GetOrCreate<ParticleEmitter>("Particles");
                emitter.Editable = false;
                emitter.Serialize = false;
                emitter.ParticlesPerSecond.Value = 100;
                emitter.ParticleType.Value = "SnakeSparks";
                emitter.Add(new Binding<Vector3>(emitter.Position, chase.Position));
                emitter.Add(new Binding<bool, string>(emitter.Enabled, x => x != "Suspended", ai.CurrentState));

                positionLight.Add(new Binding<Vector3>(positionLight.Position, chase.Position));
                emitter.Add(new Binding<Vector3>(emitter.Position, chase.Position));
                agent.Add(new Binding<Vector3>(agent.Position, chase.Position));
            }

            AI.Task checkMap = new AI.Task
            {
                Action = delegate()
                {
                    if (enemy.Map.Value.Target == null || !enemy.Map.Value.Target.Active)
                        result.Delete.Execute();
                },
            };

            AI.Task checkOperationalRadius = new AI.Task
            {
                Interval = 2.0f,
                Action = delegate()
                {
                    bool shouldBeActive = (chase.Position.Value - main.Camera.Position).Length() < operationalRadius || (enemy.Map.Value.Target.Get<Map>().GetAbsolutePosition(enemy.BaseBoxes.First().GetCoords().First()) - main.Camera.Position).Length() < operationalRadius;
                    if (shouldBeActive && ai.CurrentState == "Suspended")
                        ai.CurrentState.Value = "Idle";
                    else if (!shouldBeActive && ai.CurrentState != "Suspended")
                        ai.CurrentState.Value = "Suspended";
                },
            };

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

            chase.Add(new CommandBinding<Map, Map.Coordinate>(chase.Moved, delegate(Map m, Map.Coordinate c)
            {
                if (chase.Active)
                {
                    if (m[c].ID != criticalState.ID)
                    {
                        bool regenerate = m.Empty(c);
                        regenerate |= m.Fill(c, fillState);
                        if (regenerate)
                            m.Regenerate();
                    }
                    Sound.PlayCue(main, "SnakeMove", chase.Position);

                    if (path.Count > 0)
                    {
                        chase.Coord.Value = path[0];
                        path.RemoveAt(0);
                    }
                }
            }));

            Property<Map.Coordinate> crushCoordinate = result.GetOrMakeProperty<Map.Coordinate>("CrushCoordinate");

            ai.Setup
            (
                new AI.State
                {
                    Name = "Suspended",
                    Tasks = new[] { checkOperationalRadius },
                },
                new AI.State
                {
                    Name = "Idle",
                    Tasks = new[]
                    {
                        checkMap,
                        checkOperationalRadius,
                        new AI.Task
                        {
                            Interval = 1.0f,
                            Action = delegate()
                            {
                                Agent a = Agent.Query(chase.Position, 30.0f, 10.0f, x => x.Entity.Type == "Player");
                                if (a != null)
                                    ai.CurrentState.Value = "Alert";
                            },
                        },
                    },
                },
                new AI.State
                {
                    Name = "Alert",
                    Enter = delegate(AI.State previous)
                    {
                        chase.Enabled.Value = false;
                    },
                    Exit = delegate(AI.State next)
                    {
                        chase.Enabled.Value = true;
                    },
                    Tasks = new[]
                    {
                        checkMap,
                        checkOperationalRadius,
                        new AI.Task
                        {
                            Interval = 1.0f,
                            Action = delegate()
                            {
                                if (ai.TimeInCurrentState > 3.0f)
                                    ai.CurrentState.Value = "Idle";
                                else
                                {
                                    Agent a = Agent.Query(chase.Position, 30.0f, 20.0f, x => x.Entity.Type == "Player");
                                    if (a != null)
                                    {
                                        targetAgent.Value = a.Entity;
                                        ai.CurrentState.Value = "Chase";
                                    }
                                }
                            },
                        },
                    },
                },
                new AI.State
                {
                    Name = "Chase",
                    Enter = delegate(AI.State previousState)
                    {
                        chase.TargetActive.Value = true;
                    },
                    Exit = delegate(AI.State nextState)
                    {
                        chase.TargetActive.Value = false;
                    },
                    Tasks = new[]
                    {
                        checkMap,
                        checkOperationalRadius,
                        checkTargetAgent,
                        new AI.Task
                        {
                            Interval = 0.07f,
                            Action = delegate()
                            {
                                Vector3 targetPosition = targetAgent.Value.Target.Get<Agent>().Position;

                                float targetDistance = (targetPosition - chase.Position).Length();
                                if (targetDistance > 50.0f || ai.TimeInCurrentState > 40.0f) // He got away
                                    ai.CurrentState.Value = "Alert";
                                else if (targetDistance < 5.0f) // We got 'im
                                    ai.CurrentState.Value = "Crush";
                                else
                                    chase.Target.Value = targetPosition;
                            },
                        },
                    },
                },
                new AI.State
                {
                    Name = "Crush",
                    Enter = delegate(AI.State lastState)
                    {
                        // Set up cage
                        Map.Coordinate center = enemy.Map.Value.Target.Get<Map>().GetCoordinate(targetAgent.Value.Target.Get<Agent>().Position);

                        int radius = 1;

                        // Bottom
                        for (int x = center.X - radius; x <= center.X + radius; x++)
                        {
                            for (int z = center.Z - radius; z <= center.Z + radius; z++)
                                path.Add(new Map.Coordinate { X = x, Y = center.Y - 4, Z = z });
                        }

                        // Outer shell
                        radius = 2;
                        for (int y = center.Y - 3; y <= center.Y + 3; y++)
                        {
                            // Left
                            for (int z = center.Z - radius; z <= center.Z + radius; z++)
                                path.Add(new Map.Coordinate { X = center.X - radius, Y = y, Z = z });

                            // Right
                            for (int z = center.Z - radius; z <= center.Z + radius; z++)
                                path.Add(new Map.Coordinate { X = center.X + radius, Y = y, Z = z });

                            // Backward
                            for (int x = center.X - radius; x <= center.X + radius; x++)
                                path.Add(new Map.Coordinate { X = x, Y = y, Z = center.Z - radius });

                            // Forward
                            for (int x = center.X - radius; x <= center.X + radius; x++)
                                path.Add(new Map.Coordinate { X = x, Y = y, Z = center.Z + radius });
                        }

                        // Top
                        for (int x = center.X - radius; x <= center.X + radius; x++)
                        {
                            for (int z = center.Z - radius; z <= center.Z + radius; z++)
                                path.Add(new Map.Coordinate { X = x, Y = center.Y + 3, Z = z });
                        }

                        chase.EnablePathfinding.Value = false;
                        chase.Speed.Value = 125.0f;

                        crushCoordinate.Value = chase.Coord;
                    },
                    Exit = delegate(AI.State nextState)
                    {
                        chase.EnablePathfinding.Value = true;
                        chase.Speed.Value = 8.0f;
                        chase.Coord.Value = chase.LastCoord.Value = crushCoordinate;
                        path.Clear();
                    },
                    Tasks = new[]
                    {
                        checkMap,
                        checkOperationalRadius,
                        checkTargetAgent,
                        new AI.Task
                        {
                            Interval = 0.01f,
                            Action = delegate()
                            {
                                Agent a = targetAgent.Value.Target.Get<Agent>();
                                a.Health.Value -= 0.01f / 1.5f; // seconds to kill
                                if (!a.Active)
                                    ai.CurrentState.Value = "Alert";
                                else
                                {
                                    if ((a.Position - chase.Position.Value).Length() > 5.0f) // They're getting away
                                        ai.CurrentState.Value = "Chase";
                                }
                            }
                        }
                    },
                }
            );

            this.SetMain(result, main);
        }
Example #3
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            PointLight light = result.GetOrCreate<PointLight>("PointLight");
            light.Serialize = false;

            const float defaultLightAttenuation = 15.0f;
            light.Attenuation.Value = defaultLightAttenuation;

            Transform transform = result.GetOrCreate<Transform>("Transform");
            light.Add(new Binding<Vector3>(light.Position, transform.Position));

            VoxelChaseAI chase = result.GetOrCreate<VoxelChaseAI>("VoxelChaseAI");

            chase.Filter = delegate(Map.CellState state)
            {
                return state.ID == 0 ? VoxelChaseAI.Cell.Empty : VoxelChaseAI.Cell.Filled;
            };

            chase.Add(new TwoWayBinding<Vector3>(transform.Position, chase.Position));
            result.Add(new CommandBinding(chase.Delete, result.Delete));

            Sound sound = result.GetOrCreate<Sound>("LoopSound");
            sound.Serialize = false;
            sound.Cue.Value = "Orb Loop";
            sound.Is3D.Value = true;
            sound.IsPlaying.Value = true;
            sound.Add(new Binding<Vector3>(sound.Position, chase.Position));
            Property<float> volume = sound.GetProperty("Volume");
            Property<float> pitch = sound.GetProperty("Pitch");

            const float defaultVolume = 0.5f;
            volume.Value = defaultVolume;

            AI ai = result.GetOrCreate<AI>();

            Model model = result.GetOrCreate<Model>();
            model.Add(new Binding<Matrix>(model.Transform, transform.Matrix));
            model.Filename.Value = "Models\\sphere";
            model.Editable = false;
            model.Serialize = false;

            const float defaultModelScale = 0.25f;
            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);
                    case "Idle":
                        return new Vector3(1.0f, 1.0f, 1.0f);
                    default:
                        return new Vector3(0.0f, 0.0f, 0.0f);
                }
            }, ai.CurrentState));

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

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

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

            Agent agent = result.GetOrCreate<Agent>();
            agent.Add(new Binding<Vector3>(agent.Position, chase.Position));

            Property<int> operationalRadius = result.GetOrMakeProperty<int>("OperationalRadius", true, 100);

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

            const float sightDistance = 30.0f;
            const float hearingDistance = 15.0f;

            ai.Add(new AI.State
            {
                Name = "Idle",
                Enter = delegate(AI.State previous)
                {
                    chase.Speed.Value = 3.0f;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action = delegate()
                        {
                            Agent a = Agent.Query(chase.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                            if (a != null)
                                ai.CurrentState.Value = "Alert";
                        },
                    },
                },
            });

            Property<Entity.Handle> targetAgent = result.GetOrMakeProperty<Entity.Handle>("TargetAgent");

            ai.Add(new AI.State
            {
                Name = "Alert",
                Enter = delegate(AI.State previous)
                {
                    chase.Enabled.Value = false;
                },
                Exit = delegate(AI.State next)
                {
                    chase.Enabled.Value = true;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Interval = 1.0f,
                        Action = delegate()
                        {
                            if (ai.TimeInCurrentState > 3.0f)
                                ai.CurrentState.Value = "Idle";
                            else
                            {
                                Agent a = Agent.Query(chase.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player");
                                if (a != null)
                                {
                                    targetAgent.Value = a.Entity;
                                    ai.CurrentState.Value = "Chase";
                                }
                            }
                        },
                    },
                },
            });

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

            ai.Add(new AI.State
            {
                Name = "Chase",
                Enter = delegate(AI.State previous)
                {
                    chase.Speed.Value = 10.0f;
                    chase.TargetActive.Value = true;
                },
                Exit = delegate(AI.State next)
                {
                    chase.TargetActive.Value = false;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    checkTargetAgent,
                    new AI.Task
                    {
                        Action = delegate()
                        {
                            Entity target = targetAgent.Value.Target;
                            Vector3 targetPosition = target.Get<Transform>().Position;
                            chase.Target.Value = targetPosition;
                            if ((targetPosition - chase.Position).Length() < 10.0f)
                                ai.CurrentState.Value = "Explode";
                        }
                    }
                },
            });

            ListProperty<Map.Coordinate> coordQueue = result.GetOrMakeListProperty<Map.Coordinate>("CoordQueue");

            Property<Map.Coordinate> explosionOriginalCoord = result.GetOrMakeProperty<Map.Coordinate>("ExplosionOriginalCoord");

            ai.Add(new AI.State
            {
                Name = "Explode",
                Enter = delegate(AI.State previous)
                {
                    chase.Speed.Value = 5.0f;
                    coordQueue.Clear();
                    chase.EnablePathfinding.Value = false;

                    Map map = chase.Map.Value.Target.Get<Map>();

                    Map.Coordinate coord = chase.Coord.Value;

                    Direction toSupport = Direction.None;

                    foreach (Direction dir in DirectionExtensions.Directions)
                    {
                        if (map[coord.Move(dir)].ID != 0)
                        {
                            toSupport = dir;
                            break;
                        }
                    }

                    if (toSupport == Direction.None)
                    {
                        // Try again with the last coord
                        coord = chase.LastCoord.Value;
                        foreach (Direction dir in DirectionExtensions.Directions)
                        {
                            if (map[coord.Move(dir)].ID != 0)
                            {
                                toSupport = dir;
                                break;
                            }
                        }
                        if (toSupport == Direction.None)
                        {
                            ai.CurrentState.Value = "Idle";
                            return;
                        }
                    }

                    Direction up = toSupport.GetReverse();

                    explosionOriginalCoord.Value = coord;

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

                    for (Map.Coordinate y = coord.Clone(); y.GetComponent(up) < coord.GetComponent(up) + 3; y = y.Move(up))
                    {
                        for (Map.Coordinate x = y.Clone(); x.GetComponent(right) < coord.GetComponent(right) + 2; x = x.Move(right))
                        {
                            for (Map.Coordinate z = x.Clone(); z.GetComponent(forward) < coord.GetComponent(forward) + 2; z = z.Move(forward))
                                coordQueue.Add(z);
                        }
                    }
                },
                Exit = delegate(AI.State next)
                {
                    coordQueue.Clear();
                    chase.EnablePathfinding.Value = true;
                    chase.LastCoord.Value = chase.Coord.Value = explosionOriginalCoord;
                },
                Tasks = new[]
                {
                    checkOperationalRadius,
                    new AI.Task
                    {
                        Action = delegate()
                        {
                            volume.Value = MathHelper.Lerp(defaultVolume, 1.0f, ai.TimeInCurrentState.Value / 2.0f);
                            pitch.Value = MathHelper.Lerp(0.0f, 0.5f, ai.TimeInCurrentState.Value / 2.0f);
                            if (coordQueue.Count == 0)
                            {
                                // Explode
                                ai.CurrentState.Value = "Exploding";
                            }
                        },
                    },
                },
            });

            Property<bool> exploded = result.GetOrMakeProperty<bool>("Exploded");

            ai.Add(new AI.State
            {
                Name = "Exploding",
                Enter = delegate(AI.State previous)
                {
                    chase.EnablePathfinding.Value = false;
                    exploded.Value = false;
                    sound.Stop.Execute(AudioStopOptions.AsAuthored);
                },
                Exit = delegate(AI.State next)
                {
                    chase.EnablePathfinding.Value = true;
                    exploded.Value = false;
                    volume.Value = defaultVolume;
                    pitch.Value = 0.0f;
                    sound.Play.Execute();
                },
                Tasks = new[]
                {
                    new AI.Task
                    {
                        Interval = 0.1f,
                        Action = delegate()
                        {
                            const int radius = 8;

                            float timeInCurrentState = ai.TimeInCurrentState;
                            if (timeInCurrentState > 1.0f && !exploded)
                            {
                                Map map = chase.Map.Value.Target.Get<Map>();
                                Explosion.Explode(main, map, chase.Coord, radius, 18.0f);
                                exploded.Value = true;
                            }

                            if (timeInCurrentState > 2.0f)
                            {
                                Map map = chase.Map.Value.Target.Get<Map>();
                                Map.Coordinate? closestCell = map.FindClosestFilledCell(chase.Coord, radius + 1);
                                if (closestCell.HasValue)
                                {
                                    chase.Blend.Value = 0.0f;
                                    chase.Coord.Value = closestCell.Value;
                                    ai.CurrentState.Value = "Alert";
                                }
                                else
                                    result.Delete.Execute();
                            }
                        },
                    },
                },
            });

            EffectBlockFactory factory = Factory.Get<EffectBlockFactory>();
            Map.CellState snakeState = WorldFactory.StatesByName["Snake"];
            chase.Add(new CommandBinding<Map, Map.Coordinate>(chase.Moved, delegate(Map m, Map.Coordinate c)
            {
                if (chase.Active)
                {
                    if (coordQueue.Count > 0)
                    {
                        Map.Coordinate coord = chase.Coord.Value = coordQueue[0];
                        coordQueue.RemoveAt(0);

                        Entity block = factory.CreateAndBind(main);
                        snakeState.ApplyToEffectBlock(block.Get<ModelInstance>());

                        Map map = chase.Map.Value.Target.Get<Map>();

                        block.GetProperty<Vector3>("Offset").Value = map.GetRelativePosition(coord);

                        Vector3 absolutePos = map.GetAbsolutePosition(coord);

                        block.GetProperty<Vector3>("StartPosition").Value = absolutePos + new Vector3(0.05f, 0.1f, 0.05f);
                        block.GetProperty<Matrix>("StartOrientation").Value = Matrix.CreateRotationX(0.15f) * Matrix.CreateRotationY(0.15f);
                        block.GetProperty<float>("TotalLifetime").Value = 0.05f;
                        factory.Setup(block, chase.Map.Value.Target, coord, snakeState.ID);
                        main.Add(block);
                    }
                }
            }));

            this.SetMain(result, main);
        }