Esempio n. 1
0
        public override Entity Create(Main main)
        {
            Entity result = new Entity(main, "PointLight");

            result.Add("Transform", new Transform());
            PointLight pointLight = new PointLight();
            pointLight.Attenuation.Value = 10.0f;
            pointLight.Color.Value = Vector3.One;
            result.Add("PointLight", pointLight);

            return result;
        }
Esempio n. 2
0
        public override void Awake()
        {
            base.Awake();

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

            this.EnabledWhenPaused = false;

            if (main.EditorEnabled)
            {
                this.BlockQueue.Clear();
            }

            this.particles = ParticleSystem.Get(main, "WhiteShatter");

            for (int i = 0; i < maxSparkLights; i++)
            {
                PointLight light = new PointLight();
                light.Serialize     = false;
                light.Color.Value   = new Vector3(1.0f);
                light.Enabled.Value = false;
                this.Entity.Add(light);
                this.sparkLights.Add(light);
            }

            if (!this.main.EditorEnabled)
            {
                this.Add(new CommandBinding <Voxel, IEnumerable <Voxel.Coord>, Voxel>(Voxel.GlobalCellsFilled, delegate(Voxel map, IEnumerable <Voxel.Coord> coords, Voxel transferredFromMap)
                {
                    foreach (Voxel.Coord c in coords)
                    {
                        Voxel.t id = c.Data.ID;
                        if (id == Voxel.t.Powered || id == Voxel.t.PoweredSwitch || id == Voxel.t.HardPowered || id == Voxel.t.Infected || id == Voxel.t.HardInfected ||
                            (transferredFromMap == null && (id == Voxel.t.Blue || id == Voxel.t.Neutral || id == Voxel.t.Hard)))
                        {
                            Voxel.Coord newCoord = c;
                            newCoord.Data        = Voxel.States.Empty;
                            int generation;
                            EffectBlock.Entry generationsKey = new EffectBlock.Entry {
                                Voxel = map, Coordinate = newCoord
                            };
                            if (this.generations.TryGetValue(generationsKey, out generation))
                            {
                                this.generations.Remove(generationsKey);
                            }
                            if (!this.isInQueue(map.Entity, newCoord, false))
                            {
                                this.BlockQueue.Add(new ScheduledBlock
                                {
                                    Voxel      = map.Entity,
                                    Coordinate = newCoord,
                                    Time       = propagateDelay,
                                    Generation = generation,
                                });
                            }
                        }
                    }
                }));

                this.Add(new CommandBinding <Voxel, IEnumerable <Voxel.Coord>, Voxel>(Voxel.GlobalCellsEmptied, delegate(Voxel map, IEnumerable <Voxel.Coord> coords, Voxel transferringToNewMap)
                {
                    foreach (Voxel.Coord coord in coords)
                    {
                        Voxel.t id = coord.Data.ID;
                        if (id == Voxel.t.Powered || id == Voxel.t.PoweredSwitch || id == Voxel.t.HardPowered || id == Voxel.t.PermanentPowered)
                        {
                            this.removedPoweredCoords.Add(coord);
                        }
                        if (transferringToNewMap != null)
                        {
                            continue;
                        }

                        if (id == Voxel.t.Critical)                         // Critical. Explodes when destroyed.
                        {
                            Explosion.Explode(main, map, coord);
                        }
                        else if (id == Voxel.t.Powered || id == Voxel.t.Blue || id == Voxel.t.Neutral || id == Voxel.t.Infected || id == Voxel.t.Floater)
                        {
                            int generation;
                            Voxel.Coord c = coord;
                            c.Data        = Voxel.States.Empty;
                            EffectBlock.Entry generationKey = new EffectBlock.Entry {
                                Voxel = map, Coordinate = c
                            };
                            if (this.generations.TryGetValue(generationKey, out generation))
                            {
                                this.generations.Remove(generationKey);
                            }

                            if (id == Voxel.t.Floater)
                            {
                                Entity blockEntity      = this.blockFactory.CreateAndBind(main);
                                EffectBlock effectBlock = blockEntity.Get <EffectBlock>();
                                coord.Data.ApplyToEffectBlock(blockEntity.Get <ModelInstance>());
                                effectBlock.Delay            = 4.0f;
                                effectBlock.Offset.Value     = map.GetRelativePosition(coord);
                                effectBlock.StartPosition    = map.GetAbsolutePosition(coord) + new Vector3(2.5f, 5.0f, 2.5f);
                                effectBlock.StartOrientation = Quaternion.CreateFromYawPitchRoll(1.0f, 1.0f, 0);
                                effectBlock.TotalLifetime    = 0.5f;
                                effectBlock.Setup(map.Entity, coord, coord.Data.ID);
                                main.Add(blockEntity);
                            }

                            if (generation == 0)
                            {
                                if (!this.isInQueue(map.Entity, coord, true))
                                {
                                    this.BlockQueue.Add(new ScheduledBlock
                                    {
                                        Voxel      = map.Entity,
                                        Coordinate = coord,
                                        Time       = propagateDelay,
                                        Removing   = true,
                                    });
                                }
                            }
                            else if (generation < maxGenerations)
                            {
                                Direction down = map.GetRelativeDirection(Direction.NegativeY);
                                for (int i = 0; i < 6; i++)
                                {
                                    Direction dir        = DirectionExtensions.Directions[i];
                                    Voxel.Coord adjacent = coord.Move(dir);
                                    if (!coords.Contains(adjacent))
                                    {
                                        Voxel.t adjacentID     = map[adjacent].ID;
                                        bool adjacentIsFloater = adjacentID == Voxel.t.Floater;
                                        if (dir != down || adjacentIsFloater)
                                        {
                                            if (adjacentID == Voxel.t.Powered || adjacentID == Voxel.t.Blue || adjacentID == Voxel.t.Neutral || adjacentID == Voxel.t.Infected || adjacentIsFloater)
                                            {
                                                if (!this.isInQueue(map.Entity, adjacent, true))
                                                {
                                                    this.BlockQueue.Add(new ScheduledBlock
                                                    {
                                                        Voxel      = map.Entity,
                                                        Coordinate = adjacent,
                                                        Time       = propagateDelay,
                                                        Removing   = true,
                                                        Generation = generation + 1,
                                                    });
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else if (id == Voxel.t.White || id == Voxel.t.Glass)                         // Shatter effects.
                        {
                            ParticleSystem shatter = ParticleSystem.Get(main, "WhiteShatter");
                            Vector3 pos            = map.GetAbsolutePosition(coord);
                            for (int i = 0; i < 50; i++)
                            {
                                Vector3 offset = new Vector3((float)this.random.NextDouble() - 0.5f, (float)this.random.NextDouble() - 0.5f, (float)this.random.NextDouble() - 0.5f);
                                shatter.AddParticle(pos + offset, offset);
                            }
                            float time = this.main.TotalTime;
                            if (time - this.lastShatterSound > 0.3f)
                            {
                                this.lastShatterSound = time;
                                Sound.PostEvent(AK.EVENTS.PLAY_WHITE_SHATTER, pos);
                            }
                        }
                    }

                    if (this.removedPoweredCoords.Count > 0)
                    {
                        IEnumerable <IEnumerable <Voxel.Box> > poweredIslands = map.GetAdjacentIslands(this.removedPoweredCoords, x => x.ID == Voxel.t.Powered || x.ID == Voxel.t.HardPowered, x => x == Voxel.States.PermanentPowered || x == Voxel.States.PoweredSwitch);
                        List <Voxel.Coord> poweredCoords = poweredIslands.SelectMany(x => x).SelectMany(x => x.GetCoords()).ToList();
                        if (poweredCoords.Count > 0)
                        {
                            lock (map.MutationLock)
                            {
                                map.Empty(poweredCoords, true, true, map, false);
                                for (int i = 0; i < poweredCoords.Count; i++)
                                {
                                    Voxel.Coord coord = poweredCoords[i];
                                    if (coord.Data.ID == Voxel.t.HardPowered)
                                    {
                                        map.Fill(coord, Voxel.States.Hard, true, map);
                                    }
                                    else
                                    {
                                        map.Fill(coord, Voxel.States.Blue, true, map);
                                    }
                                }
                            }
                            this.toRegenerate.Add(map);
                        }
                        this.removedPoweredCoords.Clear();
                    }
                }));
            }
        }
Esempio n. 3
0
        public void Update(float dt)
        {
            float sparkLightFade = sparkLightBrightness * dt / sparkLightFadeTime;

            for (int i = 0; i < activeSparkLights; i++)
            {
                PointLight light = this.sparkLights[i];
                float      a     = light.Color.Value.X - sparkLightFade;
                if (a < 0.0f)
                {
                    light.Enabled.Value = false;
                    PointLight swap = this.sparkLights[activeSparkLights - 1];
                    this.sparkLights[i] = swap;
                    this.sparkLights[activeSparkLights - 1] = light;
                    activeSparkLights--;
                    oldestSparkLight = activeSparkLights;
                }
                else
                {
                    light.Color.Value = new Vector3(a);
                }
            }

            for (int i = 0; i < this.BlockQueue.Length; i++)
            {
                ScheduledBlock entry = this.BlockQueue[i];
                entry.Time -= dt;
                if (entry.Time < 0.0f)
                {
                    this.BlockQueue.RemoveAt(i);
                    i--;

                    Entity mapEntity = entry.Voxel.Target;
                    if (mapEntity != null && mapEntity.Active)
                    {
                        Voxel       map = mapEntity.Get <Voxel>();
                        Voxel.Coord c   = entry.Coordinate;
                        Voxel.t     id  = map[c].ID;

                        bool regenerate = false;

                        if (entry.Removing)
                        {
                            if (entry.Generation == 0 && id == 0)
                            {
                                Direction down = map.GetRelativeDirection(Direction.NegativeY);
                                for (int j = 0; j < 6; j++)
                                {
                                    Direction   dir               = DirectionExtensions.Directions[j];
                                    Voxel.Coord adjacent          = c.Move(dir);
                                    Voxel.t     adjacentID        = map[adjacent].ID;
                                    bool        adjacentIsFloater = adjacentID == Voxel.t.Floater;
                                    if (dir != down || adjacentIsFloater)
                                    {
                                        if (adjacentID == Voxel.t.Powered || adjacentID == Voxel.t.Blue || adjacentID == Voxel.t.Neutral || adjacentID == Voxel.t.Infected || adjacentIsFloater)
                                        {
                                            if (!this.isInQueue(map.Entity, adjacent, true))
                                            {
                                                this.BlockQueue.Add(new ScheduledBlock
                                                {
                                                    Voxel      = map.Entity,
                                                    Coordinate = adjacent,
                                                    Time       = propagateDelay,
                                                    Removing   = true,
                                                    Generation = 1,
                                                });
                                            }
                                        }
                                    }
                                }
                            }
                            else if (entry.Generation > 0 && (id == Voxel.t.Blue || id == Voxel.t.Infected || id == Voxel.t.Powered || id == Voxel.t.PermanentPowered || id == Voxel.t.HardPowered || id == Voxel.t.PoweredSwitch || id == Voxel.t.Neutral || id == Voxel.t.Floater))
                            {
                                this.generations[new EffectBlock.Entry {
                                                     Voxel = map, Coordinate = c
                                                 }] = entry.Generation;
                                map.Empty(c);
                                this.SparksLowPriority(map.GetAbsolutePosition(c), Spark.Burn);
                                regenerate = true;
                            }
                        }
                        else if (id == Voxel.t.Blue)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                Direction   dir        = DirectionExtensions.Directions[j];
                                Voxel.Coord adjacent   = c.Move(dir);
                                Voxel.t     adjacentID = map[adjacent].ID;

                                if (adjacentID == Voxel.t.Powered || adjacentID == Voxel.t.PermanentPowered || adjacentID == Voxel.t.HardPowered || adjacentID == Voxel.t.PoweredSwitch)
                                {
                                    map.Empty(c, false, true, map);
                                    map.Fill(c, Voxel.States.Powered);
                                    this.SparksLowPriority(map.GetAbsolutePosition(c), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Neutral && entry.Generation < maxGenerations)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    this.generations[new EffectBlock.Entry {
                                                         Voxel = map, Coordinate = adjacent
                                                     }] = entry.Generation + 1;
                                    map.Fill(adjacent, Voxel.States.Blue);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                            }
                        }
                        else if (id == Voxel.t.Neutral)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                Direction   dir        = DirectionExtensions.Directions[j];
                                Voxel.Coord adjacent   = c.Move(dir);
                                Voxel.t     adjacentID = map[adjacent].ID;
                                if (adjacentID == Voxel.t.Infected || adjacentID == Voxel.t.Blue || adjacentID == Voxel.t.Powered)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    map.Fill(adjacent, Voxel.States.Neutral);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.HardInfected)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    map.Fill(adjacent, Voxel.States.Hard);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                            }
                        }
                        else if (id == Voxel.t.Hard)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                Direction   dir        = DirectionExtensions.Directions[j];
                                Voxel.Coord adjacent   = c.Move(dir);
                                Voxel.t     adjacentID = map[adjacent].ID;
                                if (adjacentID == Voxel.t.HardInfected)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    map.Fill(adjacent, Voxel.States.Hard);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                            }
                        }
                        else if (id == Voxel.t.Powered || id == Voxel.t.PermanentPowered || id == Voxel.t.HardPowered || id == Voxel.t.PoweredSwitch)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                Direction   dir        = DirectionExtensions.Directions[j];
                                Voxel.Coord adjacent   = c.Move(dir);
                                Voxel.t     adjacentID = map[adjacent].ID;

                                if (id == Voxel.t.Powered && adjacentID == Voxel.t.Neutral && entry.Generation < maxGenerations)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    this.generations[new EffectBlock.Entry {
                                                         Voxel = map, Coordinate = adjacent
                                                     }] = entry.Generation + 1;
                                    map.Fill(adjacent, Voxel.States.Powered);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Blue)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    map.Fill(adjacent, Voxel.States.Powered);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Switch)
                                {
                                    map.Empty(adjacent, true, true, map);
                                    map.Fill(adjacent, Voxel.States.PoweredSwitch);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Hard)
                                {
                                    map.Empty(adjacent, true, true, map);
                                    map.Fill(adjacent, Voxel.States.HardPowered);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Normal);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Critical)
                                {
                                    map.Empty(adjacent);
                                    regenerate = true;
                                }
                            }
                        }
                        else if (id == Voxel.t.Infected || id == Voxel.t.HardInfected)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                Direction   dir        = DirectionExtensions.Directions[j];
                                Voxel.Coord adjacent   = c.Move(dir);
                                Voxel.t     adjacentID = map[adjacent].ID;
                                if (adjacentID == Voxel.t.Neutral && entry.Generation < maxGenerations)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    this.generations[new EffectBlock.Entry {
                                                         Voxel = map, Coordinate = adjacent
                                                     }] = entry.Generation + 1;
                                    map.Fill(adjacent, Voxel.States.Infected);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Dangerous);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Hard && entry.Generation < maxGenerations)
                                {
                                    map.Empty(adjacent, false, true, map);
                                    this.generations[new EffectBlock.Entry {
                                                         Voxel = map, Coordinate = adjacent
                                                     }] = entry.Generation + 1;
                                    map.Fill(adjacent, Voxel.States.HardInfected);
                                    this.SparksLowPriority(map.GetAbsolutePosition(adjacent), Spark.Dangerous);
                                    regenerate = true;
                                }
                                else if (adjacentID == Voxel.t.Critical)
                                {
                                    map.Empty(adjacent);
                                    regenerate = true;
                                }
                            }
                        }

                        if (regenerate)
                        {
                            this.toRegenerate.Add(map);
                        }
                    }
                }
            }
            for (int i = 0; i < this.toRegenerate.Count; i++)
            {
                this.toRegenerate[i].Regenerate();
            }
            this.toRegenerate.Clear();
        }
Esempio n. 4
0
        public static void Attach(Main main, Entity entity, Player player, AnimatedModel model, FPSInput input, Phone phone, Property <bool> enableWalking, Property <bool> phoneActive, Property <bool> noteActive)
        {
            UIRenderer phoneUi = entity.GetOrCreate <UIRenderer>("PhoneUI");

            model["Phone"].Speed = model["VRPhone"].Speed = model["Note"].Speed = model["VRNote"].Speed = 0.25f;

            const float phoneWidth = 200.0f;

            phoneUi.RenderTargetBackground.Value = Microsoft.Xna.Framework.Color.White;
            phoneUi.RenderTargetSize.Value       = new Point((int)phoneWidth, (int)(phoneWidth * 2.0f));
            phoneUi.Serialize     = false;
            phoneUi.Enabled.Value = false;
#if VR
            if (main.VR)
            {
                phoneUi.Reticle.Tint.Value = new Color(0.0f, 0.0f, 0.0f);
            }
#endif

            Model phoneModel = entity.GetOrCreate <Model>("PhoneModel");
            phoneModel.Filename.Value = "Models\\phone";
            phoneModel.Color.Value    = new Vector3(0.13f, 0.13f, 0.13f);
            phoneModel.Serialize      = false;
            phoneModel.Enabled.Value  = false;

            Property <Matrix> phoneBone = model.GetBoneTransform("Phone");
            phoneModel.Add(new Binding <Matrix>(phoneModel.Transform, () => phoneBone.Value * model.Transform, phoneBone, model.Transform));

            Model screen = entity.GetOrCreate <Model>("Screen");
            screen.Filename.Value = "Models\\plane";
            screen.Add(new Binding <Microsoft.Xna.Framework.Graphics.RenderTarget2D>(screen.GetRenderTarget2DParameter("Diffuse" + Model.SamplerPostfix), phoneUi.RenderTarget));
            screen.Add(new Binding <Matrix>(screen.Transform, x => Matrix.CreateTranslation(0.015f, 0.0f, 0.0f) * x, phoneModel.Transform));
            screen.Serialize     = false;
            screen.Enabled.Value = false;

            PointLight phoneLight = entity.Create <PointLight>();
            phoneLight.Serialize         = false;
            phoneLight.Enabled.Value     = false;
            phoneLight.Attenuation.Value = 0.5f;
            phoneLight.Add(new Binding <Vector3, Matrix>(phoneLight.Position, x => x.Translation, screen.Transform));

            PointLight noteLight = entity.Create <PointLight>();
            noteLight.Serialize         = false;
            noteLight.Enabled.Value     = false;
            noteLight.Attenuation.Value = 1.0f;
            noteLight.Color.Value       = new Vector3(0.3f);
            noteLight.Add(new Binding <Vector3>(noteLight.Position, () => Vector3.Transform(new Vector3(0.25f, 0.0f, 0.0f), phoneBone.Value * model.Transform), phoneBone, model.Transform));

            const float screenScale = 0.0007f;
            screen.Scale.Value = new Vector3(1.0f, (float)phoneUi.RenderTargetSize.Value.Y * screenScale, (float)phoneUi.RenderTargetSize.Value.X * screenScale);

            // Transform screen space mouse position into 3D, then back into the 2D space of the phone UI
            Property <Matrix> screenTransform = new Property <Matrix>();
            screen.Add(new Binding <Matrix>(screenTransform, () => Matrix.CreateScale(screen.Scale) * screen.Transform, screen.Scale, screen.Transform));
            phoneUi.Setup3D(screenTransform);

            // Phone UI

            const float padding      = 8.0f;
            const float messageWidth = phoneWidth - padding * 2.0f;

            Func <Property <Color>, string, float, Container> makeButton = delegate(Property <Color> color, string text, float width)
            {
                Container bg = new Container();
                bg.Tint.Value          = color;
                bg.PaddingBottom.Value = bg.PaddingLeft.Value = bg.PaddingRight.Value = bg.PaddingTop.Value = padding * 0.5f;
                bg.Add(new Binding <Color>(bg.Tint, () => bg.Highlighted ? new Color(color.Value.ToVector4() + new Vector4(0.2f, 0.2f, 0.2f, 0.0f)) : color, bg.Highlighted, color));

                TextElement msg = new TextElement();
                msg.Name.Value      = "Text";
                msg.FontFile.Value  = main.Font;
                msg.Text.Value      = text;
                msg.WrapWidth.Value = width;
                bg.Children.Add(msg);
                return(bg);
            };

            Action <Container, float> centerButton = delegate(Container button, float width)
            {
                TextElement text = (TextElement)button.Children[0];
                text.AnchorPoint.Value = new Vector2(0.5f, 0);
                text.Add(new Binding <Vector2>(text.Position, x => new Vector2(x.X * 0.5f, padding), button.Size));
                button.ResizeHorizontal.Value = false;
                button.ResizeVertical.Value   = false;
                button.Size.Value             = new Vector2(width, 36.0f * main.FontMultiplier);
            };

            Func <UIComponent, bool, Container> makeAlign = delegate(UIComponent component, bool right)
            {
                Container container = new Container();
                container.Opacity.Value          = 0.0f;
                container.PaddingBottom.Value    = container.PaddingLeft.Value = container.PaddingRight.Value = container.PaddingTop.Value = 0.0f;
                container.ResizeHorizontal.Value = false;
                container.Size.Value             = new Vector2(messageWidth, 0.0f);
                component.AnchorPoint.Value      = new Vector2(right ? 1.0f : 0.0f, 0.0f);
                component.Position.Value         = new Vector2(right ? messageWidth : 0.0f, 0.0f);
                container.Children.Add(component);
                return(container);
            };

            Property <Color> incomingColor = new Property <Color> {
                Value = new Color(0.0f, 0.0f, 0.0f, 1.0f)
            };
            Property <Color> outgoingColor = new Property <Color> {
                Value = new Color(0.0f, 0.175f, 0.35f, 1.0f)
            };
            Property <Color> alternateSenderColor = new Property <Color> {
                Value = new Color(0.25f, 0.0f, 0.25f, 1.0f)
            };
            Property <Color> composeColor = new Property <Color> {
                Value = new Color(0.5f, 0.0f, 0.0f, 1.0f)
            };
            Property <Color> disabledColor = new Property <Color> {
                Value = new Color(0.35f, 0.35f, 0.35f, 1.0f)
            };
            Property <Color> topBarColor = new Property <Color> {
                Value = new Color(0.15f, 0.15f, 0.15f, 1.0f)
            };

            Container topBarContainer = new Container();
            topBarContainer.ResizeHorizontal.Value = false;
            topBarContainer.Size.Value             = new Vector2(phoneUi.RenderTargetSize.Value.X, 0.0f);
            topBarContainer.Tint.Value             = topBarColor;
            phoneUi.Root.Children.Add(topBarContainer);

            ListContainer phoneTopBar = new ListContainer();
            phoneTopBar.Orientation.Value = ListContainer.ListOrientation.Horizontal;
            phoneTopBar.Spacing.Value     = padding;
            topBarContainer.Children.Add(phoneTopBar);

            Sprite signalIcon = new Sprite();
            signalIcon.Image.Value = "Images\\signal";
            phoneTopBar.Children.Add(signalIcon);

            TextElement noService = new TextElement();
            noService.FontFile.Value = main.Font;
            noService.Text.Value     = "\\no service";
            phoneTopBar.Children.Add(noService);

            signalIcon.Add(new Binding <bool>(signalIcon.Visible, () => player.SignalTower.Value.Target != null || phone.ActiveAnswers.Length > 0 || phone.Schedules.Length > 0, player.SignalTower, phone.ActiveAnswers.Length, phone.Schedules.Length));
            noService.Add(new Binding <bool>(noService.Visible, x => !x, signalIcon.Visible));

            ListContainer tabs = new ListContainer();
            tabs.Orientation.Value = ListContainer.ListOrientation.Horizontal;
            tabs.Spacing.Value     = 0;
            phoneUi.Root.Children.Add(tabs);

            Property <Color> messageTabColor = new Property <Color> {
                Value = outgoingColor
            };
            phoneUi.Add(new Binding <Color, Phone.Mode>(messageTabColor, x => x == Phone.Mode.Messages ? outgoingColor : topBarColor, phone.CurrentMode));
            Container messageTab = makeButton(messageTabColor, "\\messages", phoneUi.RenderTargetSize.Value.X * 0.5f - padding);
            centerButton(messageTab, phoneUi.RenderTargetSize.Value.X * 0.5f);
            tabs.Children.Add(messageTab);
            messageTab.Add(new CommandBinding(messageTab.MouseLeftUp, delegate()
            {
                phone.CurrentMode.Value = Phone.Mode.Messages;
            }));

            Property <Color> photoTabColor = new Property <Color> {
                Value = topBarColor
            };
            phoneUi.Add(new Binding <Color>(photoTabColor, delegate()
            {
                if (phone.CurrentMode == Phone.Mode.Photos)
                {
                    return(outgoingColor);
                }
                else if (string.IsNullOrEmpty(phone.Photo))
                {
                    return(disabledColor);
                }
                else
                {
                    return(topBarColor);
                }
            }, phone.CurrentMode, phone.Photo));
            Container photoTab = makeButton(photoTabColor, "\\photos", phoneUi.RenderTargetSize.Value.X * 0.5f - padding);
            centerButton(photoTab, phoneUi.RenderTargetSize.Value.X * 0.5f);
            tabs.Children.Add(photoTab);
            photoTab.Add(new CommandBinding(photoTab.MouseLeftUp, delegate()
            {
                if (!string.IsNullOrEmpty(phone.Photo))
                {
                    phone.CurrentMode.Value = Phone.Mode.Photos;
                }
            }));

            tabs.Add(new Binding <Vector2>(tabs.Position, x => new Vector2(0, x.Y), topBarContainer.Size));

            ListContainer messageLayout = new ListContainer();
            messageLayout.Spacing.Value     = padding;
            messageLayout.Orientation.Value = ListContainer.ListOrientation.Vertical;
            messageLayout.Add(new Binding <Vector2>(messageLayout.Position, () => new Vector2(padding, topBarContainer.Size.Value.Y + tabs.Size.Value.Y), topBarContainer.Size, tabs.Size));
            messageLayout.Add(new Binding <Vector2>(messageLayout.Size, () => new Vector2(phoneUi.RenderTargetSize.Value.X - padding * 2.0f, phoneUi.RenderTargetSize.Value.Y - padding - topBarContainer.Size.Value.Y - tabs.Size.Value.Y), phoneUi.RenderTargetSize, topBarContainer.Size, tabs.Size));
            messageLayout.Add(new Binding <bool, Phone.Mode>(messageLayout.Visible, x => x == Phone.Mode.Messages, phone.CurrentMode));
            phoneUi.Root.Children.Add(messageLayout);

            Container photoLayout = new Container();
            photoLayout.Opacity.Value     = 0;
            photoLayout.PaddingLeft.Value = photoLayout.PaddingRight.Value = photoLayout.PaddingTop.Value = photoLayout.PaddingBottom.Value = 0;
            photoLayout.Add(new Binding <Vector2>(photoLayout.Position, () => new Vector2(0, topBarContainer.Size.Value.Y + tabs.Size.Value.Y), topBarContainer.Size, tabs.Size));
            photoLayout.Add(new Binding <Vector2>(photoLayout.Size, () => new Vector2(phoneUi.RenderTargetSize.Value.X, phoneUi.RenderTargetSize.Value.Y - topBarContainer.Size.Value.Y - tabs.Size.Value.Y), phoneUi.RenderTargetSize, topBarContainer.Size, tabs.Size));
            photoLayout.Add(new Binding <bool>(photoLayout.Visible, x => !x, messageLayout.Visible));
            phoneUi.Root.Children.Add(photoLayout);

            Sprite photoImage = new Sprite();
            photoImage.AnchorPoint.Value = new Vector2(0.5f, 0.5f);
            photoImage.Add(new Binding <string>(photoImage.Image, phone.Photo));
            photoImage.Add(new Binding <Vector2>(photoImage.Position, x => x * 0.5f, photoLayout.Size));
            photoLayout.Children.Add(photoImage);

            Container   composeButton = makeButton(composeColor, "\\compose", messageWidth - padding * 2.0f);
            TextElement composeText   = (TextElement)composeButton.GetChildByName("Text");
            composeText.Add(new Binding <string, bool>(composeText.Text, x => x ? "\\compose gamepad" : "\\compose", main.GamePadConnected));
            UIComponent composeAlign = makeAlign(composeButton, true);

            Scroller phoneScroll = new Scroller();
            phoneScroll.ResizeVertical.Value = false;
            phoneScroll.Add(new Binding <Vector2>(phoneScroll.Size, () => new Vector2(messageLayout.Size.Value.X, messageLayout.Size.Value.Y - messageLayout.Spacing.Value - composeAlign.ScaledSize.Value.Y), messageLayout.Size, messageLayout.Spacing, composeAlign.ScaledSize));

            messageLayout.Children.Add(phoneScroll);
            messageLayout.Children.Add(composeAlign);

            ListContainer msgList = new ListContainer();
            msgList.Spacing.Value             = padding * 0.5f;
            msgList.Orientation.Value         = ListContainer.ListOrientation.Vertical;
            msgList.ResizePerpendicular.Value = false;
            msgList.Size.Value = new Vector2(messageWidth, 0.0f);
            phoneScroll.Children.Add(msgList);

            Container answerContainer = new Container();
            answerContainer.PaddingBottom.Value = answerContainer.PaddingLeft.Value = answerContainer.PaddingRight.Value = answerContainer.PaddingTop.Value = padding;
            answerContainer.Tint.Value          = incomingColor;
            answerContainer.AnchorPoint.Value   = new Vector2(1.0f, 1.0f);
            answerContainer.Add(new Binding <Vector2>(answerContainer.Position, () => composeAlign.GetAbsolutePosition() + new Vector2(composeAlign.ScaledSize.Value.X, 0), composeAlign.Position, composeAlign.ScaledSize));
            phoneUi.Root.Children.Add(answerContainer);
            answerContainer.Visible.Value = false;

            ListContainer answerList = new ListContainer();
            answerList.Orientation.Value = ListContainer.ListOrientation.Vertical;
            answerList.Alignment.Value   = ListContainer.ListAlignment.Max;
            answerContainer.Children.Add(answerList);

            int selectedAnswer = 0;

            composeButton.Add(new CommandBinding(composeButton.MouseLeftUp, delegate()
            {
                answerContainer.Visible.Value = !answerContainer.Visible;
                if (answerContainer.Visible && main.GamePadConnected)
                {
                    selectedAnswer = 0;
                    foreach (UIComponent answer in answerList.Children)
                    {
                        answer.Highlighted.Value = false;
                    }
                    answerList.Children[0].Highlighted.Value = true;
                }
            }));

            tabs.Add(new Binding <bool>(tabs.EnableInput, () => !main.Paused && !answerContainer.Visible, answerContainer.Visible, main.Paused));
            msgList.Add(new Binding <bool>(msgList.EnableInput, () => !main.Paused && !answerContainer.Visible, answerContainer.Visible, main.Paused));
            answerContainer.Add(new Binding <bool>(answerContainer.EnableInput, x => !x, main.Paused));
            composeButton.Add(new Binding <bool>(composeButton.EnableInput, x => !x, main.Paused));

            Action scrollToBottom = delegate()
            {
                // HACK
                Animation scroll = new Animation
                                   (
                    new Animation.Delay(0.01f),
                    new Animation.Execute(delegate()
                {
                    phoneScroll.ScrollToBottom();
                })
                                   );
                entity.Add(scroll);
            };

            // Note

            UIRenderer noteUi = entity.GetOrCreate <UIRenderer>("NoteUI");

            const float noteWidth = 400.0f;

            noteUi.RenderTargetBackground.Value = new Microsoft.Xna.Framework.Color(0.8f, 0.75f, 0.7f);
            noteUi.RenderTargetSize.Value       = new Point((int)noteWidth, (int)(noteWidth * 1.29f));       // 8.5x11 aspect ratio
            noteUi.Serialize     = false;
            noteUi.Enabled.Value = false;

            Model noteModel = entity.GetOrCreate <Model>("Note");
            noteModel.Filename.Value = "Models\\note";
            noteModel.Add(new Binding <Microsoft.Xna.Framework.Graphics.RenderTarget2D>(noteModel.GetRenderTarget2DParameter("Diffuse" + Model.SamplerPostfix), noteUi.RenderTarget));
            noteModel.Add(new Binding <Matrix>(noteModel.Transform, x => Matrix.CreateTranslation(-0.005f, 0.05f, 0.08f) * x, phoneModel.Transform));
            noteModel.Serialize     = false;
            noteModel.Enabled.Value = false;

            Container togglePhoneMessage = null;

            entity.Add(new NotifyBinding(delegate()
            {
                bool hasSignalTower = (player.SignalTower.Value.Target != null && player.SignalTower.Value.Target.Active && !string.IsNullOrEmpty(player.SignalTower.Value.Target.Get <SignalTower>().Initial));
                if (hasSignalTower)
                {
                    phone.Enabled.Value = true;
                }

                bool hasNoteOrSignalTower = (player.Note.Value.Target != null && player.Note.Value.Target.Active) || hasSignalTower;

                if (togglePhoneMessage == null && hasNoteOrSignalTower)
                {
                    togglePhoneMessage = main.Menu.ShowMessage(entity, hasSignalTower ? "\\signal tower prompt" : "\\note prompt");
                }
                else if (togglePhoneMessage != null && !hasNoteOrSignalTower && !phoneActive && !noteActive)
                {
                    main.Menu.HideMessage(entity, togglePhoneMessage);
                    togglePhoneMessage = null;
                }
            }, player.Note, player.SignalTower));

            entity.Add(new CommandBinding(entity.Delete, delegate()
            {
                main.Menu.HideMessage(null, togglePhoneMessage);
            }));

            // Note UI

            const float notePadding = 40.0f;

            ListContainer noteLayout = new ListContainer();
            noteLayout.Spacing.Value     = padding;
            noteLayout.Orientation.Value = ListContainer.ListOrientation.Vertical;
            noteLayout.Alignment.Value   = ListContainer.ListAlignment.Min;
            noteLayout.Position.Value    = new Vector2(notePadding, notePadding);
            noteLayout.Add(new Binding <Vector2, Point>(noteLayout.Size, x => new Vector2(x.X - notePadding * 2.0f, x.Y - notePadding * 2.0f), noteUi.RenderTargetSize));
            noteUi.Root.Children.Add(noteLayout);

            Sprite noteUiImage = new Sprite();
            noteLayout.Children.Add(noteUiImage);

            TextElement noteUiText = new TextElement();
            noteUiText.FontFile.Value = main.Font;
            noteUiText.Tint.Value     = new Microsoft.Xna.Framework.Color(0.1f, 0.1f, 0.1f);
            noteUiText.Add(new Binding <float, Vector2>(noteUiText.WrapWidth, x => x.X, noteLayout.Size));
            noteLayout.Children.Add(noteUiText);

            // Toggle note
            Animation noteAnim = null;

            float         startRotationY = 0;
            Action <bool> showNote       = delegate(bool show)
            {
                model.Stop("Phone", "Note", "VRPhone", "VRNote");
                Entity noteEntity = player.Note.Value.Target;
                noteActive.Value = show && noteEntity != null;
                Note note = noteEntity != null?noteEntity.Get <Note>() : null;

                if (noteActive)
                {
                    input.EnableLook.Value  = input.EnableMouse.Value = false;
                    enableWalking.Value     = false;
                    noteModel.Enabled.Value = true;
                    noteUi.Enabled.Value    = true;
                    noteLight.Enabled.Value = true;
                    Session.Recorder.Event(main, "Note", note.Text);
                    noteUiImage.Image.Value = note.Image;
                    noteUiText.Text.Value   = note.Text;
                    string noteAnimation;
#if VR
                    if (main.VR)
                    {
                        noteAnimation = "VRNote";
                    }
                    else
#endif
                    noteAnimation = "Note";

                    model.StartClip(noteAnimation, 6, true, AnimatedModel.DefaultBlendTime * 2.0f);
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_NOTE_PICKUP, entity);

                    if (noteAnim != null && noteAnim.Active)
                    {
                        noteAnim.Delete.Execute();
                    }
                    else
                    {
                        startRotationY = input.Mouse.Value.Y;
                    }
                    // Level the player's view
                    noteAnim = new Animation
                               (
                        new Animation.Ease
                        (
                            new Animation.Custom(delegate(float x)
                    {
                        input.Mouse.Value = new Vector2(input.Mouse.Value.X, startRotationY * (1.0f - x));
                    }, 0.5f),
                            Animation.Ease.EaseType.OutQuadratic
                        )
                               );
                    entity.Add(noteAnim);
                }
                else
                {
                    enableWalking.Value = true;
                    if (note != null)
                    {
                        Session.Recorder.Event(main, "NoteEnd");
                    }
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_NOTE_DROP, entity);
                    if (note != null && !note.IsCollected)
                    {
                        note.IsCollected.Value = true;
                    }

                    // Return the player's view
                    if (noteAnim != null && noteAnim.Active)
                    {
                        noteAnim.Delete.Execute();
                    }
                    noteAnim = new Animation
                               (
                        new Animation.Ease
                        (
                            new Animation.Custom(delegate(float x)
                    {
                        input.Mouse.Value = new Vector2(input.Mouse.Value.X, startRotationY * x);
                    }, 0.5f),
                            Animation.Ease.EaseType.OutQuadratic
                        ),
                        new Animation.Execute(delegate()
                    {
                        noteModel.Enabled.Value = false;
                        noteUi.Enabled.Value    = false;
                        noteLight.Enabled.Value = false;
                        input.EnableLook.Value  = input.EnableMouse.Value = true;
                    })
                               );
                    entity.Add(noteAnim);
                }
            };

            // Toggle phone

            Animation phoneAnim = null;

            Action <bool> showPhone = delegate(bool show)
            {
                if (togglePhoneMessage != null)
                {
                    main.Menu.HideMessage(entity, togglePhoneMessage);
                    togglePhoneMessage = null;
                }

                if (show || (phone.Schedules.Length == 0 && !phone.WaitForAnswer))
                {
                    phoneActive.Value             = show;
                    answerContainer.Visible.Value = false;

                    model.Stop("Phone", "Note", "VRPhone", "VRNote");
                    if (phoneActive)
                    {
                        phoneUi.IsMouseVisible.Value = true;
                        enableWalking.Value          = false;
                        phoneModel.Enabled.Value     = true;
                        screen.Enabled.Value         = true;
                        phoneUi.Enabled.Value        = true;
                        phoneLight.Enabled.Value     = true;
                        input.EnableLook.Value       = input.EnableMouse.Value = false;
                        Session.Recorder.Event(main, "Phone");
                        phoneScroll.CheckLayout();
                        scrollToBottom();

                        string phoneAnimation;
#if VR
                        if (main.VR)
                        {
                            phoneAnimation = "VRPhone";
                        }
                        else
#endif
                        phoneAnimation = "Phone";

                        model.StartClip(phoneAnimation, 6, true, AnimatedModel.DefaultBlendTime * 2.0f);

                        // Level the player's view
                        if (phoneAnim != null && phoneAnim.Active)
                        {
                            phoneAnim.Delete.Execute();
                        }
                        else
                        {
                            startRotationY = input.Mouse.Value.Y;
                        }
                        phoneAnim = new Animation
                                    (
                            new Animation.Ease
                            (
                                new Animation.Custom(delegate(float x)
                        {
                            input.Mouse.Value = new Vector2(input.Mouse.Value.X, startRotationY * (1.0f - x));
                        }, 0.5f),
                                Animation.Ease.EaseType.OutQuadratic
                            )
                                    );
                        entity.Add(phoneAnim);
                    }
                    else
                    {
                        Session.Recorder.Event(main, "PhoneEnd");
                        enableWalking.Value          = true;
                        phoneUi.IsMouseVisible.Value = false;

                        // Return the player's view
                        if (phoneAnim != null && phoneAnim.Active)
                        {
                            phoneAnim.Delete.Execute();
                        }
                        phoneAnim = new Animation
                                    (
                            new Animation.Ease
                            (
                                new Animation.Custom(delegate(float x)
                        {
                            input.Mouse.Value = new Vector2(input.Mouse.Value.X, startRotationY * x);
                        }, 0.5f),
                                Animation.Ease.EaseType.OutQuadratic
                            ),
                            new Animation.Execute(delegate()
                        {
                            phoneModel.Enabled.Value = false;
                            screen.Enabled.Value     = false;
                            phoneUi.Enabled.Value    = false;
                            phoneLight.Enabled.Value = false;
                            input.EnableLook.Value   = input.EnableMouse.Value = true;
                        })
                                    );
                        entity.Add(phoneAnim);
                    }
                }
            };

            input.Bind(main.Settings.TogglePhone, PCInput.InputState.Down, delegate()
            {
                // Special hack to prevent phone toggling when you're trying to open the Steam overlay
                if (main.Settings.TogglePhone.Value.Key == Keys.Tab && input.GetKey(Keys.LeftShift))
                {
                    return;
                }

                if (noteActive || phoneActive || phone.CanReceiveMessages)
                {
                    if (!phoneActive && (noteActive || player.Note.Value.Target != null))
                    {
                        showNote(!noteActive);
                    }
                    else if (phone.Enabled)
                    {
                        showPhone(!phoneActive);
                    }
                }
            });

            phone.Add(new CommandBinding(phone.Show, delegate()
            {
                phone.Enabled.Value = true;
                if (!phoneActive)
                {
                    showPhone(true);
                }
            }));

            // Gamepad code for the phone

            input.Add(new CommandBinding(input.GetButtonUp(Buttons.A), () => phoneActive && composeButton.Visible && phone.CurrentMode.Value == Phone.Mode.Messages, delegate()
            {
                if (answerContainer.Visible)
                {
                    answerList.Children[selectedAnswer].MouseLeftUp.Execute();
                }
                else
                {
                    composeButton.MouseLeftUp.Execute();
                }
            }));

            input.Add(new CommandBinding(input.GetButtonUp(Buttons.B), () => phoneActive && answerContainer.Visible, delegate()
            {
                answerContainer.Visible.Value = false;
            }));

            const float  moveInterval   = 0.1f;
            const float  switchInterval = 0.2f;
            float        lastScroll     = 0;
            float        lastModeSwitch = 0;
            Action <int> scrollPhone    = delegate(int delta)
            {
                if (main.TotalTime - lastScroll > moveInterval &&
                    main.TotalTime - lastModeSwitch > switchInterval)
                {
                    if (answerContainer.Visible)
                    {
                        answerList.Children[selectedAnswer].Highlighted.Value = false;
                        selectedAnswer += delta;
                        while (selectedAnswer < 0)
                        {
                            selectedAnswer += answerList.Children.Length;
                        }
                        while (selectedAnswer > answerList.Children.Length - 1)
                        {
                            selectedAnswer -= answerList.Children.Length;
                        }
                        answerList.Children[selectedAnswer].Highlighted.Value = true;
                    }
                    else
                    {
                        phoneScroll.MouseScrolled.Execute(delta * -4);
                    }
                    lastScroll = main.TotalTime;
                }
            };

            Action switchMode = delegate()
            {
                if (main.TotalTime - lastScroll > switchInterval &&
                    main.TotalTime - lastModeSwitch > moveInterval)
                {
                    Phone.Mode current  = phone.CurrentMode;
                    Phone.Mode nextMode = current == Phone.Mode.Messages ? Phone.Mode.Photos : Phone.Mode.Messages;
                    if (nextMode == Phone.Mode.Photos && string.IsNullOrEmpty(phone.Photo))
                    {
                        nextMode = Phone.Mode.Messages;
                    }
                    phone.CurrentMode.Value = nextMode;
                    lastModeSwitch          = main.TotalTime;
                }
            };
            input.Add(new CommandBinding(input.GetButtonDown(Buttons.LeftThumbstickLeft), () => phoneActive && !answerContainer.Visible, switchMode));
            input.Add(new CommandBinding(input.GetButtonDown(Buttons.LeftThumbstickRight), () => phoneActive && !answerContainer.Visible, switchMode));
            input.Add(new CommandBinding(input.GetButtonDown(Buttons.DPadLeft), () => phoneActive && !answerContainer.Visible, switchMode));
            input.Add(new CommandBinding(input.GetButtonDown(Buttons.DPadRight), () => phoneActive && !answerContainer.Visible, switchMode));

            input.Add(new CommandBinding(input.GetButtonDown(Buttons.LeftThumbstickUp), () => phoneActive, delegate()
            {
                scrollPhone(-1);
            }));

            input.Add(new CommandBinding(input.GetButtonDown(Buttons.DPadUp), () => phoneActive, delegate()
            {
                scrollPhone(-1);
            }));

            input.Add(new CommandBinding(input.GetButtonDown(Buttons.LeftThumbstickDown), () => phoneActive, delegate()
            {
                scrollPhone(1);
            }));

            input.Add(new CommandBinding(input.GetButtonDown(Buttons.DPadDown), () => phoneActive, delegate()
            {
                scrollPhone(1);
            }));

            Func <Phone.Sender, Property <Color> > messageColor = delegate(Phone.Sender sender)
            {
                switch (sender)
                {
                case Phone.Sender.Player:
                    return(outgoingColor);

                case Phone.Sender.A:
                    return(incomingColor);

                default:
                    return(alternateSenderColor);
                }
            };

            msgList.Add(new ListBinding <UIComponent, Phone.Message>
                        (
                            msgList.Children,
                            phone.Messages,
                            delegate(Phone.Message msg)
            {
                return(makeAlign(makeButton(messageColor(msg.Sender), "\\" + msg.Name, messageWidth - padding * 2.0f), msg.Sender == Phone.Sender.Player));
            }
                        ));

            Action <float, Container> animateMessage = delegate(float delay, Container msg)
            {
                msg.CheckLayout();
                Vector2 originalSize = msg.Size;
                msg.Size.Value = new Vector2(0, originalSize.Y);
                entity.Add(new Animation
                           (
                               new Animation.Delay(delay),
                               new Animation.Ease(new Animation.Vector2MoveTo(msg.Size, originalSize, 0.5f), Animation.Ease.EaseType.OutExponential)
                           ));
            };

            Container typingIndicator = null;

            Action showTypingIndicator = delegate()
            {
                typingIndicator = makeAlign(makeButton(incomingColor, "\\...", messageWidth - padding * 2.0f), false);
                msgList.Children.Add(typingIndicator);
                animateMessage(0.2f, typingIndicator);
            };

            if (phone.Schedules.Length > 0)
            {
                showTypingIndicator();
            }

            answerList.Add(new ListBinding <UIComponent, Phone.Ans>
                           (
                               answerList.Children,
                               phone.ActiveAnswers,
                               delegate(Phone.Ans answer)
            {
                UIComponent button = makeButton(outgoingColor, "\\" + answer.Name, messageWidth - padding * 4.0f);
                button.Add(new CommandBinding(button.MouseLeftUp, delegate()
                {
                    if (!phone.WaitForAnswer)                             // If we're not waiting for an answer, the player must be initiating a conversation
                    {
                        // This is the start of a conversation
                        // Disable the signal tower if we're in range
                        Entity s = player.SignalTower.Value.Target;
                        if (s != null)
                        {
                            s.Get <SignalTower>().Initial.Value = null;
                        }
                    }

                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_PHONE_SEND, entity);
                    phone.Answer(answer);

                    scrollToBottom();
                    if (phone.Schedules.Length == 0)                             // No more messages incoming
                    {
                        if (togglePhoneMessage == null)
                        {
                            togglePhoneMessage = main.Menu.ShowMessage(entity, "\\phone done prompt");
                        }
                    }
                    else
                    {
                        // More messages incoming
                        showTypingIndicator();
                    }
                }));
                return(button);
            }
                           ));

            Action refreshComposeButtonVisibility = delegate()
            {
                bool show = phone.ActiveAnswers.Length > 0 && phone.Schedules.Length == 0;
                answerContainer.Visible.Value &= show;
                composeButton.Visible.Value    = show;
                selectedAnswer = 0;
            };
            composeButton.Add(new ListNotifyBinding <Phone.Ans>(refreshComposeButtonVisibility, phone.ActiveAnswers));
            composeButton.Add(new ListNotifyBinding <Phone.Schedule>(refreshComposeButtonVisibility, phone.Schedules));
            refreshComposeButtonVisibility();

            entity.Add(new CommandBinding(phone.MessageReceived, delegate()
            {
                if (typingIndicator != null)
                {
                    typingIndicator.Delete.Execute();
                    typingIndicator = null;
                }

                if (phone.Schedules.Length > 0)
                {
                    showTypingIndicator();
                }

                float delay;
                if (phoneActive)
                {
                    scrollToBottom();
                    delay = 0;
                }
                else
                {
                    showPhone(true);
                    delay = 0.5f;
                }

                // Animate the new message
                animateMessage(delay, (Container)msgList.Children[msgList.Children.Length - 1].Children[0]);

                AkSoundEngine.PostEvent(AK.EVENTS.PLAY_PHONE_VIBRATE, entity);
                if (togglePhoneMessage == null && phone.Schedules.Length == 0 && phone.ActiveAnswers.Length == 0)                 // No more messages incoming, and no more answers to give
                {
                    togglePhoneMessage = main.Menu.ShowMessage(entity, "[{{TogglePhone}}]");
                }
            }));

            if (noteActive)
            {
                showNote(true);
            }
            else if (phoneActive)
            {
                showPhone(true);
            }
        }
Esempio n. 5
0
        public override Entity Create(Main main)
        {
            Entity result = new Entity(main, "Blast");

            Transform transform = new Transform();
            result.Add("Transform", transform);

            PhysicsBlock physics = new PhysicsBlock();
            result.Add("Physics", physics);

            Model model = new Model();
            result.Add("Model", model);
            model.Filename.Value = "Models\\blast";
            model.Color.Value = new Vector3(0.75f, 2.0f, 0.75f);

            PointLight light = new PointLight();
            light.Shadowed.Value = true;
            light.Color.Value = new Vector3(model.Color.Value.X, model.Color.Value.Y, model.Color.Value.Z);
            light.Attenuation.Value = 10.0f;
            result.Add("Light", light);

            if (ParticleSystem.Get(main, "Sparks") == null)
            {
                ParticleSystem.Add(main, "Sparks",
                new ParticleSystem.ParticleSettings
                {
                    TextureName = "Particles\\spark",
                    MaxParticles = 1000,
                    Duration = TimeSpan.FromSeconds(1.0f),
                    MinHorizontalVelocity = 0.0f,
                    MaxHorizontalVelocity = 0.0f,
                    MinVerticalVelocity = 0.0f,
                    MaxVerticalVelocity = 0.0f,
                    Gravity = new Vector3(0.0f, 0.0f, 0.0f),
                    EndVelocity = 0.0f,
                    MinRotateSpeed = -20.0f,
                    MaxRotateSpeed = 20.0f,
                    MinStartSize = 0.5f,
                    MaxStartSize = 0.4f,
                    MinEndSize = 0.2f,
                    MaxEndSize = 0.1f,
                    BlendState = Microsoft.Xna.Framework.Graphics.BlendState.Additive,
                    MinColor = new Vector4(0.75f, 2.0f, 0.75f, 1.0f),
                    MaxColor = new Vector4(0.75f, 2.0f, 0.75f, 1.0f),
                });
            }

            ParticleEmitter emitter = new ParticleEmitter();
            emitter.ParticleType.Value = "Sparks";
            emitter.ParticlesPerSecond.Value = 200;
            emitter.Jitter.Value = new Vector3(0.25f);
            result.Add("Particles", emitter);

            Sound loopSound = new Sound();
            result.Add("LoopSound", loopSound);
            loopSound.Cue.Value = "Blast Loop";

            return result;
        }
Esempio n. 6
0
        public void PostProcess(RenderTarget2D result, RenderParameters parameters)
        {
            if (this.needBufferReallocation())
            {
                return;
            }

            Vector3         originalCameraPosition  = parameters.Camera.Position;
            Matrix          originalViewMatrix      = parameters.Camera.View;
            BoundingFrustum originalBoundingFrustum = parameters.Camera.BoundingFrustum;

            parameters.Camera.Position.Value = Vector3.Zero;
            Matrix newViewMatrix = originalViewMatrix;

            newViewMatrix.Translation    = Vector3.Zero;
            parameters.Camera.View.Value = newViewMatrix;

            RasterizerState originalState = this.main.GraphicsDevice.RasterizerState;

            bool enableSSAO = this.allowSSAO && this.EnableSSAO;

            if (enableSSAO)
            {
                // Down-sample depth buffer
                this.downsampleEffect.CurrentTechnique = this.downsampleEffect.Techniques["DownsampleDepth"];
                this.sources2[0]      = this.depthBuffer;
                this.sources2[1]      = this.normalBuffer;
                this.destinations2[0] = this.halfDepthBuffer;
                this.destinations2[1] = this.halfBuffer1;
                if (!this.preparePostProcess(this.sources2, this.destinations2, this.downsampleEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                // Compute SSAO
                parameters.Camera.SetParameters(this.ssaoEffect);
                this.ssaoEffect.CurrentTechnique = this.ssaoEffect.Techniques["SSAO"];
                this.sources2[0]      = this.halfDepthBuffer;
                this.sources2[1]      = this.halfBuffer1;
                this.destinations1[0] = this.halfBuffer2;
                if (!this.preparePostProcess(this.sources2, this.destinations1, this.ssaoEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                // Blur
                this.ssaoEffect.CurrentTechnique = this.ssaoEffect.Techniques["BlurHorizontal"];
                this.sources2[0]      = this.halfBuffer2;
                this.sources2[1]      = this.halfDepthBuffer;
                this.destinations1[0] = this.halfBuffer1;
                if (!this.preparePostProcess(this.sources2, this.destinations1, this.ssaoEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                this.ssaoEffect.CurrentTechnique = this.ssaoEffect.Techniques["Composite"];
                this.sources2[0]      = this.halfBuffer1;
                this.sources2[1]      = this.halfDepthBuffer;
                this.destinations1[0] = this.halfBuffer2;
                if (!this.preparePostProcess(this.sources2, this.destinations1, this.ssaoEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);
            }

            // Global lighting
            this.destinations2[0] = this.lightingBuffer;
            this.destinations2[1] = this.specularBuffer;
            if (!this.setTargets(this.destinations2))
            {
                return;
            }
            string globalLightTechnique = "GlobalLight";

            if (this.lightingManager.EnableGlobalShadowMap && this.lightingManager.HasGlobalShadowLight)
            {
                if (parameters.IsMainRender)
                {
                    if (this.lightingManager.HasGlobalShadowLightClouds)
                    {
                        if (this.lightingManager.EnableDetailGlobalShadowMap)
                        {
                            globalLightTechnique = "GlobalLightDetailShadowClouds";
                        }
                        else
                        {
                            globalLightTechnique = "GlobalLightShadowClouds";
                        }
                    }
                    else
                    {
                        if (this.lightingManager.EnableDetailGlobalShadowMap)
                        {
                            globalLightTechnique = "GlobalLightDetailShadow";
                        }
                        else
                        {
                            globalLightTechnique = "GlobalLightShadow";
                        }
                    }
                }
                else
                {
                    globalLightTechnique = "GlobalLightShadow";
                }
            }
            Renderer.globalLightEffect.CurrentTechnique = Renderer.globalLightEffect.Techniques[globalLightTechnique];
            parameters.Camera.SetParameters(Renderer.globalLightEffect);
            this.lightingManager.SetGlobalLightParameters(Renderer.globalLightEffect, parameters.Camera, originalCameraPosition);
            Renderer.globalLightEffect.Parameters["Materials"].SetValue(parameters.MaterialData);
            this.sources3[0]      = this.depthBuffer;
            this.sources3[1]      = this.normalBuffer;
            this.sources3[2]      = this.colorBuffer1;
            this.destinations2[0] = this.lightingBuffer;
            this.destinations2[1] = this.specularBuffer;
            this.setTargetParameters(this.sources3, this.destinations2, Renderer.globalLightEffect);
            this.applyEffect(Renderer.globalLightEffect);
            Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

            // Spot and point lights
            if (!parameters.ReverseCullOrder)
            {
                this.main.GraphicsDevice.RasterizerState = this.reverseCullState;
            }

            // HACK
            // Increase the far plane to prevent clipping back faces of huge lights
            float originalFarPlane = parameters.Camera.FarPlaneDistance;

            parameters.Camera.FarPlaneDistance.Value *= 4.0f;
            parameters.Camera.SetParameters(Renderer.pointLightEffect);
            parameters.Camera.SetParameters(Renderer.spotLightEffect);
            parameters.Camera.FarPlaneDistance.Value = originalFarPlane;

            // Spot lights
            Renderer.spotLightEffect.Parameters["Materials"].SetValue(parameters.MaterialData);
            this.setTargetParameters(this.sources3, this.destinations2, Renderer.spotLightEffect);
            for (int i = 0; i < SpotLight.All.Count; i++)
            {
                SpotLight light = SpotLight.All[i];
                if (!light.Enabled || light.Suspended || light.Attenuation == 0.0f || light.Color.Value.LengthSquared() == 0.0f || !originalBoundingFrustum.Intersects(light.BoundingFrustum))
                {
                    continue;
                }

                this.lightingManager.SetSpotLightParameters(light, Renderer.spotLightEffect, originalCameraPosition);
                this.applyEffect(Renderer.spotLightEffect);
                this.drawModel(Renderer.spotLightModel);
            }

            // Point lights
            Renderer.pointLightEffect.Parameters["Materials"].SetValue(parameters.MaterialData);
            this.setTargetParameters(this.sources3, this.destinations2, Renderer.pointLightEffect);
            for (int i = 0; i < PointLight.All.Count; i++)
            {
                PointLight light = PointLight.All[i];
                if (!light.Enabled || light.Suspended || light.Attenuation == 0.0f || light.Color.Value.LengthSquared() == 0.0f || !originalBoundingFrustum.Intersects(light.BoundingSphere))
                {
                    continue;
                }
                this.lightingManager.SetPointLightParameters(light, Renderer.pointLightEffect, originalCameraPosition);
                this.applyEffect(Renderer.pointLightEffect);
                this.drawModel(Renderer.pointLightModel);
            }

            if (!parameters.ReverseCullOrder)
            {
                this.main.GraphicsDevice.RasterizerState = originalState;
            }

            RenderTarget2D colorSource      = this.colorBuffer1;
            RenderTarget2D colorDestination = this.hdrBuffer2;
            RenderTarget2D colorTemp        = null;

            // Compositing
            this.compositeEffect.CurrentTechnique = this.compositeEffect.Techniques[enableSSAO ? "CompositeSSAO" : "Composite"];
            this.lightingManager.SetCompositeParameters(this.compositeEffect);
            parameters.Camera.SetParameters(this.compositeEffect);
            this.compositeEffect.Parameters["Materials"].SetValue(parameters.MaterialData);
            RenderTarget2D[] compositeSources;
            if (enableSSAO)
            {
                compositeSources    = this.sources4;
                compositeSources[3] = this.halfBuffer2;
            }
            else
            {
                compositeSources = this.sources3;
            }

            compositeSources[0] = colorSource;
            compositeSources[1] = this.lightingBuffer;
            compositeSources[2] = this.specularBuffer;

            this.destinations1[0] = colorDestination;

            if (!this.preparePostProcess(compositeSources, this.destinations1, this.compositeEffect))
            {
                return;
            }

            Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

            bool enableBloom      = this.allowBloom && this.EnableBloom;
            bool enableMotionBlur = this.MotionBlurAmount > 0.0f;

#if VR
            if (this.main.VR)
            {
                enableMotionBlur = false;
            }
#endif

            bool enableBlur = this.BlurAmount > 0.0f;

            // Swap the color buffers
            colorSource      = this.hdrBuffer2;
            colorDestination = enableBloom || this.allowToneMapping || enableBlur || enableMotionBlur ? this.hdrBuffer1 : result;

            parameters.DepthBuffer = this.depthBuffer;
            parameters.FrameBuffer = colorSource;

            // Alpha components

            // Drawing to the color destination
            this.destinations1[0] = colorDestination;
            if (!this.setTargets(this.destinations1))
            {
                return;
            }

            // Copy the color source to the destination
            this.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, originalState);
            this.spriteBatch.Draw(colorSource, Vector2.Zero, Color.White);
            this.spriteBatch.End();

            parameters.Camera.Position.Value = originalCameraPosition;
            parameters.Camera.View.Value     = originalViewMatrix;

            this.main.DrawAlphaComponents(parameters);
            this.main.DrawPostAlphaComponents(parameters);

            // Swap the color buffers
            colorTemp              = colorDestination;
            colorDestination       = colorSource;
            parameters.FrameBuffer = colorSource = colorTemp;

            // Bloom
            if (enableBloom)
            {
                this.bloomEffect.CurrentTechnique = this.bloomEffect.Techniques["Downsample"];
                this.sources1[0]      = colorSource;
                this.destinations1[0] = this.halfBuffer1;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.bloomEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                this.bloomEffect.CurrentTechnique = this.bloomEffect.Techniques["BlurHorizontal"];
                this.sources1[0]      = this.halfBuffer1;
                this.destinations1[0] = this.halfBuffer2;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.bloomEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                this.bloomEffect.CurrentTechnique = this.bloomEffect.Techniques["BlurVertical"];
                this.sources1[0]      = this.halfBuffer2;
                this.destinations1[0] = this.halfBuffer1;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.bloomEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                this.bloomEffect.CurrentTechnique = this.bloomEffect.Techniques["Composite"];
                this.sources2[0]      = colorSource;
                this.sources2[1]      = this.halfBuffer1;
                this.destinations1[0] = enableBlur || enableMotionBlur ? this.colorBuffer2 : result;
                if (!this.preparePostProcess(this.sources2, this.destinations1, this.bloomEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                // Swap the color buffers
                colorDestination = this.colorBuffer1;
                colorSource      = this.colorBuffer2;
            }
            else if (this.allowToneMapping)
            {
                this.bloomEffect.CurrentTechnique = this.bloomEffect.Techniques["ToneMapOnly"];
                this.sources1[0]      = colorSource;
                this.destinations1[0] = enableBlur || enableMotionBlur ? this.colorBuffer2 : result;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.bloomEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                // Swap the color buffers
                colorDestination = this.colorBuffer1;
                colorSource      = this.colorBuffer2;
            }

            // Motion blur
            if (enableMotionBlur)
            {
                this.motionBlurEffect.CurrentTechnique = this.motionBlurEffect.Techniques["MotionBlur"];
                parameters.Camera.SetParameters(this.motionBlurEffect);

                // If we just reallocated our buffers, don't use the velocity buffer from the last frame because it will be empty
                this.sources3[0]      = colorSource;
                this.sources3[1]      = this.normalBuffer;
                this.sources3[2]      = this.justReallocatedBuffers ? this.normalBuffer : this.normalBufferLastFrame;
                this.destinations1[0] = enableBlur ? colorDestination : result;
                if (!this.preparePostProcess(this.sources3, this.destinations1, this.motionBlurEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);

                // Swap the velocity buffers
                RenderTarget2D temp = this.normalBufferLastFrame;
                this.normalBufferLastFrame = this.normalBuffer;
                this.normalBuffer          = temp;

                // Swap the color buffers
                colorTemp        = colorDestination;
                colorDestination = colorSource;
                colorSource      = colorTemp;
            }

            if (enableBlur)
            {
                // Blur
                this.blurEffect.CurrentTechnique = this.blurEffect.Techniques["BlurHorizontal"];
                parameters.Camera.SetParameters(this.blurEffect);
                this.sources1[0]      = colorSource;
                this.destinations1[0] = colorDestination;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.blurEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);
                this.blurEffect.CurrentTechnique = this.blurEffect.Techniques["Composite"];

                // Swap the color buffers
                colorTemp        = colorDestination;
                colorDestination = colorSource;
                colorSource      = colorTemp;

                this.sources1[0]      = colorSource;
                this.destinations1[0] = result;
                if (!this.preparePostProcess(this.sources1, this.destinations1, this.blurEffect))
                {
                    return;
                }
                Renderer.quad.DrawAlpha(this.main.GameTime, RenderParameters.Default);
            }

            parameters.DepthBuffer = null;
            parameters.FrameBuffer = null;

            this.justReallocatedBuffers = false;
        }
Esempio n. 7
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);
        }
Esempio n. 8
0
        private static void explode(Main main, Voxel map, Voxel.Coord coord, Vector3 pos, int radius, float physicsRadius)
        {
            float distanceToCamera = (main.Camera.Position.Value - pos).Length();

            // Kaboom
            Sound.PostEvent(distanceToCamera < physicsRadius * 1.5f ? AK.EVENTS.PLAY_EXPLOSION_CLOSE : AK.EVENTS.PLAY_EXPLOSION, pos);

            Entity lightEntity = Factory.Get <PointLightFactory>().CreateAndBind(main);

            lightEntity.Serialize = false;
            PointLight light = lightEntity.Get <PointLight>();

            light.Color.Value       = new Vector3(1.3f, 1.1f, 0.9f);
            light.Attenuation.Value = 20.0f;
            light.Position.Value    = pos;
            lightEntity.Add(new Animation
                            (
                                new Animation.FloatMoveTo(light.Attenuation, 0.0f, 1.0f),
                                new Animation.Execute(light.Delete)
                            ));
            main.Add(lightEntity);

            SmokeFactory smokeFactory = Factory.Get <SmokeFactory>();

            for (int i = 0; i < 5; i++)
            {
                Entity smoke = smokeFactory.CreateAndBind(main);
                smoke.Get <Transform>().Position.Value = pos;
                main.Add(smoke);
            }

            ParticleEmitter.Emit(main, "Smoke", pos, physicsRadius * 0.4f, 250);

            Entity player = PlayerFactory.Instance;

            if (player != null && player.Active)
            {
                player.Get <CameraController>().Shake.Execute(pos, 50.0f);
            }

            const float physicsImpulse         = 70.0f;
            const float minPlayerDamage        = 0.1f;
            const float playerDamageMultiplier = 2.0f;

            // Remove the cells
            BlockFactory blockFactory = Factory.Get <BlockFactory>();

            foreach (Voxel m in Voxel.ActiveVoxels.ToList())
            {
                List <Voxel.Coord> removals = new List <Voxel.Coord>();

                Voxel.Coord c           = m.GetCoordinate(pos);
                Vector3     relativePos = m.GetRelativePosition(c);

                Quaternion quat = m.Entity.Get <Transform>().Quaternion;

                for (Voxel.Coord x = c.Move(Direction.NegativeX, radius - 1); x.X < c.X + radius; x.X++)
                {
                    for (Voxel.Coord y = x.Move(Direction.NegativeY, radius - 1); y.Y < c.Y + radius; y.Y++)
                    {
                        for (Voxel.Coord z = y.Move(Direction.NegativeZ, radius - 1); z.Z < c.Z + radius; z.Z++)
                        {
                            Voxel.State s = m[z];
                            if (s.ID == 0 || s.Permanent)
                            {
                                continue;
                            }

                            Vector3 cellPos = m.GetRelativePosition(z);
                            if ((cellPos - relativePos).Length() < radius - 1)
                            {
                                removals.Add(z);
                                if (random.NextDouble() > 0.5)
                                {
                                    Entity    block          = blockFactory.CreateAndBind(main);
                                    Transform blockTransform = block.Get <Transform>();
                                    blockTransform.Position.Value   = m.GetAbsolutePosition(cellPos);
                                    blockTransform.Quaternion.Value = Quaternion.CreateFromYawPitchRoll(((float)random.NextDouble() - 0.5f) * 2.0f * (float)Math.PI, ((float)random.NextDouble() - 0.5f) * 2.0f * (float)Math.PI, ((float)random.NextDouble() - 0.5f) * 2.0f * (float)Math.PI);
                                    s.ApplyToBlock(block);
                                    main.Add(block);
                                }
                            }
                        }
                    }
                }
                if (removals.Count > 0)
                {
                    m.Empty(removals);
                    m.Regenerate();
                }
            }

            // Damage the player
            if (player != null && player.Active)
            {
                Vector3 toPlayer = player.Get <Transform>().Position - pos;
                float   d        = toPlayer.Length();
                if (d < physicsRadius)
                {
                    float attenuation = 1.0f;
                    if (d > 0)
                    {
                        Voxel.GlobalRaycast(pos, toPlayer / d, d, delegate(int x, Voxel.t c)
                        {
                            Voxel.State s = Voxel.States.All[c];
                            if (s.Permanent)
                            {
                                attenuation = 0.0f;
                                return(true);
                            }
                            else if (s.Hard)
                            {
                                attenuation -= 0.6f;
                            }
                            else
                            {
                                attenuation -= 0.35f;
                            }
                            return(false);
                        });
                        attenuation = Math.Max(0, attenuation);
                    }
                    player.Get <Agent>().Damage.Execute(attenuation * (minPlayerDamage + (1.0f - (d / physicsRadius)) * playerDamageMultiplier));
                }
            }

            // Apply impulse to dynamic maps
            foreach (Voxel m in Voxel.ActiveVoxels)
            {
                DynamicVoxel dm = m as DynamicVoxel;
                if (dm == null)
                {
                    continue;
                }

                Vector3 toMap         = dm.Transform.Value.Translation - pos;
                float   distanceToMap = toMap.Length();
                toMap /= distanceToMap;

                toMap *= Math.Max(0.0f, 1.0f - (distanceToMap / physicsRadius)) * Math.Min(200.0f, dm.PhysicsEntity.Mass) * physicsImpulse;

                dm.PhysicsEntity.ApplyImpulse(dm.Transform.Value.Translation + new Vector3(((float)random.NextDouble() - 0.5f) * 2.0f, ((float)random.NextDouble() - 0.5f) * 2.0f, ((float)random.NextDouble() - 0.5f) * 2.0f), toMap);
            }

            // Apply impulse to physics blocks
            foreach (Entity b in main.Get("Block"))
            {
                PhysicsBlock block         = b.Get <PhysicsBlock>();
                Vector3      fromExplosion = b.Get <Transform>().Position.Value - pos;
                float        distance      = fromExplosion.Length();
                if (distance > 0.0f && distance < physicsRadius)
                {
                    float blend = 1.0f - (distance / physicsRadius);
                    block.LinearVelocity.Value  += fromExplosion * blend * 10.0f / distance;
                    block.AngularVelocity.Value += new Vector3(((float)random.NextDouble() - 0.5f) * 2.0f, ((float)random.NextDouble() - 0.5f) * 2.0f, ((float)random.NextDouble() - 0.5f) * 2.0f) * blend;
                }
            }
        }
Esempio n. 9
0
        public override Entity Create(Main main)
        {
            Entity result = Factory.Get<DynamicMapFactory>().Create(main);
            result.Type = "Turret";

            PointLight light = new PointLight();
            light.Shadowed.Value = true;
            light.Color.Value = new Vector3(0.75f, 2.0f, 0.75f);
            light.Attenuation.Value = 0.0f;
            light.Editable = false;
            result.Add("Light", light);

            Sound blastChargeSound = new Sound();
            blastChargeSound.Cue.Value = "Blast Charge";
            result.Add("BlastChargeSound", blastChargeSound);

            Sound blastFireSound = new Sound();
            blastFireSound.Cue.Value = "Blast Fire";
            result.Add("BlastFireSound", blastFireSound);

            result.Add("Damage", new Property<float> { Editable = true, Value = 0.1f });
            result.Add("VisibilityCheckInterval", new Property<float> { Editable = true, Value = 1.0f });
            result.Add("BlastChargeTime", new Property<float> { Editable = true, Value = 1.25f });
            result.Add("BlastInterval", new Property<float> { Editable = true, Value = 1.0f });
            result.Add("PlayerPositionMemoryTime", new Property<float> { Editable = true, Value = 4.0f });
            result.Add("BlastSpeed", new Property<float> { Editable = true, Value = 75.0f });
            result.Add("PlayerDetectionRadius", new Property<float> { Editable = true, Value = 15.0f });

            return result;
        }