コード例 #1
0
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            var knightModel = Asset.Load<Model>("knight Model");
            knight = new Entity { new ModelComponent { Model = knightModel } };
            knight.Transform.Position = new Vector3(0, 0f, 0f);
            var animationComponent = knight.GetOrCreate<AnimationComponent>();
            animationComponent.Animations.Add("Run", Asset.Load<AnimationClip>("knight Run"));
            animationComponent.Animations.Add("Idle", Asset.Load<AnimationClip>("knight Idle"));

            // We will test both non-optimized and optimized clips
            megalodonClip = CreateModelChangeAnimation(new ProceduralModelDescriptor(new CubeProceduralModel { Size = Vector3.One, MaterialInstance = { Material = knightModel.Materials[0].Material } }).GenerateModel(Services));
            knightOptimizedClip = CreateModelChangeAnimation(Asset.Load<Model>("knight Model"));
            knightOptimizedClip.Optimize();

            animationComponent.Animations.Add("ChangeModel1", megalodonClip);
            animationComponent.Animations.Add("ChangeModel2", knightOptimizedClip);

            Scene.Entities.Add(knight);

            camera = new TestCamera();
            CameraComponent = camera.Camera;
            Script.Add(camera);

            LightingKeys.EnableFixedAmbientLight(GraphicsDevice.Parameters, true);
            GraphicsDevice.Parameters.Set(EnvironmentLightKeys.GetParameterKey(LightSimpleAmbientKeys.AmbientLight, 0), (Color3)Color.White);

            camera.Position = new Vector3(6.0f, 2.5f, 1.5f);
            camera.SetTarget(knight, true);
        }
コード例 #2
0
ファイル: AnimatedModelTests.cs プロジェクト: cg123/xenko
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            var knightModel = Content.Load<Model>("knight Model");
            knight = new Entity { new ModelComponent { Model = knightModel } };
            knight.Transform.Position = new Vector3(0, 0f, 0f);
            var animationComponent = knight.GetOrCreate<AnimationComponent>();
            animationComponent.Animations.Add("Run", Content.Load<AnimationClip>("knight Run"));
            animationComponent.Animations.Add("Idle", Content.Load<AnimationClip>("knight Idle"));

            // We will test both non-optimized and optimized clips
            megalodonClip = CreateModelChangeAnimation(new ProceduralModelDescriptor(new CubeProceduralModel { Size = Vector3.One, MaterialInstance = { Material = knightModel.Materials[0].Material } }).GenerateModel(Services));
            knightOptimizedClip = CreateModelChangeAnimation(Content.Load<Model>("knight Model"));
            knightOptimizedClip.Optimize();

            animationComponent.Animations.Add("ChangeModel1", megalodonClip);
            animationComponent.Animations.Add("ChangeModel2", knightOptimizedClip);

            Scene.Entities.Add(knight);

            camera = new TestCamera();
            CameraComponent = camera.Camera;
            Script.Add(camera);

            camera.Position = new Vector3(6.0f, 2.5f, 1.5f);
            camera.SetTarget(knight, true);
        }
コード例 #3
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            Transform transform = result.GetOrCreate<Transform>("Transform");
            PhysicsBlock physics = result.GetOrCreate<PhysicsBlock>();
            physics.Size.Value = Vector3.One;
            physics.Editable = false;
            ModelInstance model = result.GetOrCreate<ModelInstance>();
            model.Editable = false;

            physics.Add(new TwoWayBinding<Matrix>(transform.Matrix, physics.Transform));

            Property<string> soundCue = result.GetOrMakeProperty<string>("CollisionSoundCue", false);
            soundCue.Serialize = false;

            model.Add(new Binding<Matrix>(model.Transform, transform.Matrix));

            const float volumeMultiplier = 0.1f;

            physics.Add(new CommandBinding<Collidable, ContactCollection>(physics.Collided, delegate(Collidable collidable, ContactCollection contacts)
            {
                float volume = contacts[contacts.Count - 1].NormalImpulse * volumeMultiplier;
                if (volume > 0.1f && soundCue.Value != null)
                {
                    Sound sound = Sound.PlayCue(main, soundCue, transform.Position, volume, 0.05f);
                    if (sound != null)
                        sound.GetProperty("Pitch").Value = 1.0f;
                }
            }));

            this.SetMain(result, main);

            Property<bool> valid = result.GetOrMakeProperty<bool>("Valid", false);
            valid.Serialize = false;

            Property<string> type = result.GetOrMakeProperty<string>("Type", true);
            type.Set = delegate(string value)
            {
                Map.CellState state;
                if (WorldFactory.StatesByName.TryGetValue(value, out state))
                {
                    state.ApplyToBlock(result);
                    valid.Value = true;
                }

                type.InternalValue = value;
            };
        }
コード例 #4
0
ファイル: VoxelFillFactory.cs プロジェクト: dsmo7206/Lemma
		public override void Bind(Entity entity, Main main, bool creating = false)
		{
			Transform mapTransform = entity.GetOrCreate<Transform>("MapTransform");
			mapTransform.Selectable.Value = false;
			Transform transform = entity.GetOrCreate<Transform>("Transform");
			VoxelFill voxelFill = entity.GetOrCreate<VoxelFill>("VoxelFill");
			voxelFill.Add(new CommandBinding(voxelFill.Delete, entity.Delete));

			Sound.AttachTracker(entity, voxelFill.RumblePosition);

			this.InternalBind(entity, main, creating, mapTransform, true);

			if (main.EditorEnabled)
			{
				Voxel voxel = entity.Get<Voxel>();

				Action refreshMapTransform = delegate()
				{
					Entity parent = voxelFill.Target.Value.Target;
					if (parent != null && parent.Active)
					{
						Voxel staticMap = parent.Get<Voxel>();
						if (staticMap == null)
							mapTransform.Matrix.Value = transform.Matrix;
						else
						{
							mapTransform.Position.Value = staticMap.GetAbsolutePosition(staticMap.GetRelativePosition(staticMap.GetCoordinate(transform.Matrix.Value.Translation)) - new Vector3(0.5f) + staticMap.Offset + voxel.Offset.Value);
							Matrix parentOrientation = staticMap.Transform;
							parentOrientation.Translation = Vector3.Zero;
							mapTransform.Quaternion.Value = Quaternion.CreateFromRotationMatrix(parentOrientation);
						}
					}
					else
						mapTransform.Matrix.Value = transform.Matrix;
				};

				entity.Add(new NotifyBinding(refreshMapTransform, transform.Matrix, voxel.Offset, voxelFill.Target));
				refreshMapTransform();
			}

			entity.Add("Enabled", voxelFill.Enabled);
			entity.Add("Enable", voxelFill.Enable);
			entity.Add("Disable", voxelFill.Disable);
			entity.Add("IntervalMultiplier", voxelFill.IntervalMultiplier);
			entity.Add("BlockLifetime", voxelFill.BlockLifetime);
		}
コード例 #5
0
ファイル: StaticSliderFactory.cs プロジェクト: dsmo7206/Lemma
		public override void Bind(Entity entity, Main main, bool creating = false)
		{
			entity.CannotSuspendByDistance = true;
			Transform transform = entity.GetOrCreate<Transform>("Transform");
			Transform mapTransform = entity.GetOrCreate<Transform>("MapTransform");
			mapTransform.Selectable.Value = false;
			StaticSlider slider = entity.GetOrCreate<StaticSlider>("StaticSlider");
			Factory.Get<VoxelFactory>().InternalBind(entity, main, creating, mapTransform);
			slider.Add(new TwoWayBinding<Matrix>(mapTransform.Matrix, slider.Transform));
			slider.Add(new Binding<Matrix>(slider.EditorTransform, transform.Matrix));

			Voxel voxel = entity.Get<Voxel>();
			slider.Add(new Binding<Vector3>(voxel.LinearVelocity, slider.LinearVelocity));

			Sound.AttachTracker(entity, voxel.Transform);
			SoundKiller.Add(entity, AK.EVENTS.STOP_ALL_OBJECT);

			if (main.EditorEnabled)
				entity.Add(new Binding<Matrix>(entity.GetOrCreate<SliderCommon>("SliderCommon").OriginalTransform, voxel.Transform));

			entity.Add("Forward", slider.Forward);
			entity.Add("Backward", slider.Backward);
			entity.Add("OnHitMax", slider.OnHitMax);
			entity.Add("OnHitMin", slider.OnHitMin);

			entity.Add("Direction", slider.Direction);
			entity.Add("Minimum", slider.Minimum);
			entity.Add("Maximum", slider.Maximum);
			entity.Add("Speed", slider.Speed);
			entity.Add("Goal", slider.Goal);
			entity.Add("StartAtMinimum", slider.StartAtMinimum);
			entity.Add("EnablePhysics", voxel.EnablePhysics);
			entity.Add("Position", slider.Position, new PropertyEntry.EditorData { Readonly = true });
			entity.Add("MovementLoop", slider.MovementLoop, new PropertyEntry.EditorData { Options = WwisePicker.Get(main) });
			entity.Add("MovementStop", slider.MovementStop, new PropertyEntry.EditorData { Options = WwisePicker.Get(main) });

			entity.Add("UVRotation", voxel.UVRotation);
			entity.Add("UVOffset", voxel.UVOffset);

			if (main.EditorEnabled)
				this.attachEditorComponents(entity, main);
		}
コード例 #6
0
ファイル: EntityAsset.cs プロジェクト: dejavvu/paradox
            public object New(Type type)
            {
                // Create a new root entity, and make sure transformation component is created
                var rootEntity = new Entity();
                rootEntity.Name = "Root";
                rootEntity.GetOrCreate(TransformComponent.Key);

                return new EntityAsset
                {
                    Hierarchy =
                    {
                        Entities = { rootEntity },
                        RootEntity = rootEntity.Id,
                    }
                };
            }
コード例 #7
0
ファイル: PickingSystem.cs プロジェクト: h78hy78yhoi8j/xenko
        public Entity GenerateRotationGizmo()
        {
            var entity = new Entity("RotationGizmo");
            entity.Set(TransformationComponent.Key, new TransformationComponent());

            // TODO: Factorize some of this code with GenerateTranslationGizmo?
            var gizmoActions = new[] { GizmoAction.RotationX, GizmoAction.RotationY, GizmoAction.RotationZ };
            var orientationMatrices = new[] { Matrix.Identity, Matrix.RotationZ((float)Math.PI * 0.5f), Matrix.RotationY(-(float)Math.PI * 0.5f) };
            var colors = new[] { Color.Green, Color.Blue, Color.Red };

            var albedoMaterial = new ShaderMixinSource()
                {
                    "AlbedoDiffuseBase",
                    "AlbedoSpecularBase",
                    new ShaderComposition("albedoDiffuse", new ShaderClassSource("ComputeColorFixed", MaterialKeys.DiffuseColor)),
                    new ShaderComposition("albedoSpecular", new ShaderClassSource("ComputeColor")), // TODO: Default values!
                };

            for (int axis = 0; axis < 3; ++axis)
            {
                // Rendering
                var circleEffectMeshData = new EffectMeshData();
                circleEffectMeshData.Parameters = new ParameterCollection();
                circleEffectMeshData.MeshData = MeshDataHelper.CreateCircle(20.0f, 32, colors[axis]);
                circleEffectMeshData.EffectData = new EffectData("Gizmo") { AlbedoMaterial = albedoMaterial };

                var circleEntity = new Entity("ArrowCone");
                circleEntity.GetOrCreate(ModelComponent.Key).SubMeshes.Add(circleEffectMeshData);
                circleEntity.Set(TransformationComponent.Key, TransformationMatrix.CreateComponent(orientationMatrices[axis]));

                circleEntity.Set(GizmoColorKey, colors[axis]);
                circleEntity.Set(GizmoActionKey, gizmoActions[axis]);

                entity.GetOrCreate(TransformationComponent.Key).Children.Add(circleEntity.GetOrCreate(TransformationComponent.Key));
            }

            return entity;
        }
コード例 #8
0
        //[XenkoScript(ScriptFlags.AssemblyStartup)]
        public static async Task SaveScene2(EngineContext engineContext)
        {
            var assetManager = new AssetManager(new AssetSerializerContextGenerator(engineContext.PackageManager));

            var entity = new Entity();
            var meshComponent = entity.GetOrCreate(ModelComponent.Key);
            meshComponent.SubMeshes.Add(new EffectMeshData { MeshData = new SubMeshData { DrawCount = 321 } });
            var entities = new[] { entity };

            throw new NotImplementedException();
            //var convertedEntities = assetManager.Convert<EntityGroup, IList<Entity>>(entities, "/data/package_scene.hotei#");
            //assetManager.Save(convertedEntities);

            //var contents = ParameterContainerExtensions.EnumerateContentData(convertedEntities).ToArray();
            //var sceneText = ParameterContainerExtensions.ConvertToText(contents[0]);
            //File.WriteAllText("current_scene.txt", sceneText);

            //ParameterContainerExtensions.ConvertFromText(engineContext, sceneText, "/data/package_scene_copy.hotei#/root");
            //var convertedEntities2 = assetManager.Load<EntityGroup>("/data/package_scene_copy.hotei#");
        }
コード例 #9
0
ファイル: JointFactory.cs プロジェクト: dsmo7206/Lemma
		public static void Bind(Entity entity, Main main, Func<BEPUphysics.Entities.Entity, BEPUphysics.Entities.Entity, Vector3, Vector3, Vector3, ISpaceObject> createJoint, bool allowRotation, bool creating = false, bool directional = true)
		{
			Transform mapTransform = entity.GetOrCreate<Transform>("MapTransform");
			mapTransform.Selectable.Value = false;

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

			Factory.Get<DynamicVoxelFactory>().InternalBind(entity, main, creating, mapTransform);

			DynamicVoxel map = entity.Get<DynamicVoxel>();

			Components.Joint jointData = entity.GetOrCreate<Components.Joint>("Joint");

			Action refreshMapTransform = delegate()
			{
				Entity parent = jointData.Parent.Value.Target;
				if (parent != null && parent.Active)
				{
					Voxel staticMap = parent.Get<Voxel>();
					jointData.Coord.Value = staticMap.GetCoordinate(transform.Matrix.Value.Translation);
					mapTransform.Position.Value = staticMap.GetAbsolutePosition(staticMap.GetRelativePosition(jointData.Coord) - new Vector3(0.5f) + staticMap.Offset + map.Offset.Value);
					if (allowRotation)
					{
						if (main.EditorEnabled)
							mapTransform.Quaternion.Value = transform.Quaternion;
					}
					else
					{
						Matrix parentOrientation = staticMap.Transform;
						parentOrientation.Translation = Vector3.Zero;
						mapTransform.Quaternion.Value = Quaternion.CreateFromRotationMatrix(parentOrientation);
					}
				}
				else
					mapTransform.Matrix.Value = transform.Matrix;
			};

			if (main.EditorEnabled)
				entity.Add(new NotifyBinding(refreshMapTransform, transform.Matrix, transform.Quaternion, map.Offset, jointData.Parent));

			ISpaceObject joint = null;
			CommandBinding jointDeleteBinding = null, parentPhysicsUpdateBinding = null;
			NotifyBinding parentStaticMoveBinding = null;

			Action updateJoint = null;

			Action rebuildJoint = null;
			rebuildJoint = delegate()
			{
				if (jointDeleteBinding != null)
					entity.Remove(jointDeleteBinding);
				jointDeleteBinding = null;

				if (parentPhysicsUpdateBinding != null)
					entity.Remove(parentPhysicsUpdateBinding);
				parentPhysicsUpdateBinding = null;

				updateJoint();
			};

			updateJoint = delegate()
			{
				if (joint != null)
				{
					if (joint.Space != null)
						main.Space.Remove(joint);
					joint = null;
				}
				if (parentStaticMoveBinding != null)
				{
					entity.Remove(parentStaticMoveBinding);
					parentStaticMoveBinding = null;
				}

				Entity parent = jointData.Parent.Value.Target;

				if (main.EditorEnabled)
					refreshMapTransform();
				else if (parent != null && parent.Active)
				{
					Voxel parentStaticMap = parent.Get<Voxel>();

					//map.PhysicsEntity.Position = mapTransform.Position;
					if (!allowRotation)
						map.PhysicsEntity.Orientation = mapTransform.Quaternion;

					if (jointData.Direction != Direction.None)
					{
						Vector3 relativeLineAnchor = parentStaticMap.GetRelativePosition(jointData.Coord) - new Vector3(0.5f) + parentStaticMap.Offset + map.Offset;
						Vector3 lineAnchor = parentStaticMap.GetAbsolutePosition(relativeLineAnchor);
						DynamicVoxel parentDynamicMap = parent.Get<DynamicVoxel>();
						joint = createJoint(map.PhysicsEntity, parentDynamicMap == null ? null : parentDynamicMap.PhysicsEntity, lineAnchor, parentStaticMap.GetAbsoluteVector(jointData.Direction.Value.GetVector()), parentStaticMap.GetAbsolutePosition(jointData.Coord));
						main.Space.Add(joint);
						map.PhysicsEntity.ActivityInformation.Activate();

						if (parentDynamicMap != null && parentPhysicsUpdateBinding == null)
						{
							parentPhysicsUpdateBinding = new CommandBinding(parentDynamicMap.PhysicsUpdated, updateJoint);
							entity.Add(parentPhysicsUpdateBinding);
						}

						if (parentDynamicMap == null && joint is PrismaticJoint)
						{
							parentStaticMoveBinding = new NotifyBinding(delegate()
							{
								PrismaticJoint prismaticJoint = (PrismaticJoint)joint;
								Vector3 a = parentStaticMap.GetAbsolutePosition(relativeLineAnchor);
								prismaticJoint.PointOnLineJoint.LineAnchor = a;
								prismaticJoint.Limit.OffsetA = a;
								prismaticJoint.Motor.OffsetA = a;
							}, parentStaticMap.Transform);
							entity.Add(parentStaticMoveBinding);
						}

						if (jointDeleteBinding == null)
						{
							jointDeleteBinding = new CommandBinding(parent.Delete, delegate()
							{
								jointData.Parent.Value = null;
							});
							entity.Add(jointDeleteBinding);
						}
					}
				}
			};
			entity.Add(new CommandBinding(map.PhysicsUpdated, updateJoint));
			entity.Add(new NotifyBinding(rebuildJoint, jointData.Parent));
			entity.Add(new CommandBinding(entity.Delete, delegate()
			{
				if (joint != null && joint.Space != null)
				{
					main.Space.Remove(joint);
					joint = null;
				}
			}));
			
			entity.Add(new CommandBinding(map.OnSuspended, delegate()
			{
				if (joint != null && joint.Space != null)
					main.Space.Remove(joint);
			}));
			entity.Add(new CommandBinding(map.OnResumed, delegate()
			{
				if (joint != null && joint.Space == null)
					main.Space.Add(joint);
			}));
			
			entity.Add(new PostInitialization(rebuildJoint));

			if (main.EditorEnabled)
				JointFactory.attachEditorComponents(entity, main, directional);
		}
コード例 #10
0
ファイル: EditorFactory.cs プロジェクト: kernelbitch/Lemma
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            this.SetMain(result, main);
            Editor editor = result.Get<Editor>();
            EditorUI ui = result.Get<EditorUI>();
            Model model = result.Get<Model>("Model");
            FPSInput input = result.Get<FPSInput>("Input");
            UIRenderer uiRenderer = result.Get<UIRenderer>();

            ModelAlpha radiusVisual = new ModelAlpha();
            radiusVisual.Filename.Value = "Models\\alpha-sphere";
            radiusVisual.Color.Value = new Vector3(1.0f);
            radiusVisual.Alpha.Value = 0.1f;
            radiusVisual.Editable = false;
            radiusVisual.Serialize = false;
            radiusVisual.DrawOrder.Value = 11; // In front of water
            radiusVisual.DisableCulling.Value = true;
            result.Add(radiusVisual);
            radiusVisual.Add(new Binding<Matrix, Vector3>(radiusVisual.Transform, x => Matrix.CreateTranslation(x), editor.Position));
            radiusVisual.Add(new Binding<Vector3, int>(radiusVisual.Scale, x => new Vector3(x), editor.BrushSize));
            radiusVisual.Add(new Binding<bool>(radiusVisual.Enabled, () => editor.BrushSize > 1 && editor.MapEditMode, editor.BrushSize, editor.MapEditMode));
            radiusVisual.CullBoundingBox.Value = false;

            ModelAlpha selection = new ModelAlpha();
            selection.Filename.Value = "Models\\alpha-box";
            selection.Color.Value = new Vector3(1.0f, 0.7f, 0.4f);
            selection.Alpha.Value = 0.25f;
            selection.Editable = false;
            selection.Serialize = false;
            selection.DrawOrder.Value = 12; // In front of water and radius visualizer
            selection.DisableCulling.Value = true;
            result.Add(selection);
            selection.Add(new Binding<bool>(selection.Enabled, editor.VoxelSelectionActive));
            selection.Add(new Binding<Matrix>(selection.Transform, delegate()
            {
                const float padding = 0.1f;
                Map map = editor.SelectedEntities[0].Get<Map>();
                Vector3 start = map.GetRelativePosition(editor.VoxelSelectionStart) - new Vector3(0.5f), end = map.GetRelativePosition(editor.VoxelSelectionEnd) - new Vector3(0.5f);
                return Matrix.CreateScale((end - start) + new Vector3(padding)) * Matrix.CreateTranslation((start + end) * 0.5f) * map.Transform;
            }, () => selection.Enabled, editor.VoxelSelectionStart, editor.VoxelSelectionEnd));
            selection.CullBoundingBox.Value = false;

            Action<string, PCInput.Chord, Func<bool>, Command> addCommand = delegate(string description, PCInput.Chord chord, Func<bool> enabled, Command action)
            {
                ui.PopupCommands.Add(new EditorUI.PopupCommand { Description = description, Chord = chord, Enabled = enabled, Action = action });

                if (chord.Modifier != Keys.None)
                    input.Add(new CommandBinding(input.GetChord(chord), () => enabled() && !ui.StringPropertyLocked, action));
                else
                    input.Add(new CommandBinding(input.GetKeyDown(chord.Key), () => enabled() && !ui.StringPropertyLocked, action));

                ui.Add(new CommandBinding(action, delegate()
                {
                    Container container = new Container();
                    container.Tint.Value = Microsoft.Xna.Framework.Color.Black;
                    container.Opacity.Value = 0.2f;
                    container.AnchorPoint.Value = new Vector2(1.0f, 0.0f);
                    container.Add(new Binding<Vector2, Point>(container.Position, x => new Vector2(x.X - 10.0f, 10.0f), main.ScreenSize));
                    TextElement display = new TextElement();
                    display.FontFile.Value = "Font";
                    display.Text.Value = description;
                    container.Children.Add(display);
                    uiRenderer.Root.Children.Add(container);
                    main.AddComponent(new Animation
                    (
                        new Animation.Parallel
                        (
                            new Animation.FloatMoveTo(container.Opacity, 0.0f, 1.0f),
                            new Animation.FloatMoveTo(display.Opacity, 0.0f, 1.0f)
                        ),
                        new Animation.Execute(delegate() { uiRenderer.Root.Children.Remove(container); })
                    ));
                }));
            };

            foreach (string key in Factory.factories.Keys)
            {
                string entityType = key;
                ui.PopupCommands.Add(new EditorUI.PopupCommand
                {
                    Description = "Add " + entityType,
                    Enabled = () => editor.SelectedEntities.Count == 0 && !editor.MapEditMode,
                    Action = new Command { Action = () => editor.Spawn.Execute(entityType) },
                });
            }

            Scroller scroller = (Scroller)uiRenderer.Root.GetChildByName("Scroller");

            Container popup = (Container)uiRenderer.Root.GetChildByName("Popup");
            ListContainer popupList = (ListContainer)popup.GetChildByName("PopupList");

            input.Add(new CommandBinding(input.GetKeyUp(Keys.Space), () => !editor.MapEditMode && !ui.StringPropertyLocked && !editor.MovementEnabled, delegate()
            {
                Vector2 pos = input.Mouse;
                pos.X = Math.Min(main.ScreenSize.Value.X - popup.Size.Value.X, pos.X);
                pos.Y = Math.Min(main.ScreenSize.Value.Y - popup.Size.Value.Y, pos.Y);
                popup.Position.Value = pos;
                ui.PopupVisible.Value = true;
            }));

            input.Add(new CommandBinding(input.GetKeyUp(Keys.Escape), () => ui.PopupVisible, delegate()
            {
                if (ui.PopupSearchText.Value == "_")
                    ui.PopupVisible.Value = false;
                else
                    ui.ClearSelectedStringProperty();
            }));

            input.Add(new CommandBinding(input.RightMouseButtonUp, () => ui.PopupVisible, delegate()
            {
                ui.PopupVisible.Value = false;
            }));

            uiRenderer.Add(new Binding<bool>(popup.Visible, ui.PopupVisible));
            uiRenderer.Add(new Binding<string>(((TextElement)popup.GetChildByName("PopupSearch")).Text, ui.PopupSearchText));
            uiRenderer.Add(new ListBinding<UIComponent>(popupList.Children, ui.PopupElements));

            AudioListener audioListener = result.Get<AudioListener>();
            audioListener.Add(new Binding<Vector3>(audioListener.Position, main.Camera.Position));
            audioListener.Add(new Binding<Vector3>(audioListener.Forward, main.Camera.Forward));

            model.Add(new Binding<bool>(model.Enabled, editor.MapEditMode));
            model.Add(new Binding<Matrix>(model.Transform, () => editor.Orientation.Value * Matrix.CreateTranslation(editor.Position), editor.Position, editor.Orientation));

            editor.Add(new TwoWayBinding<string>(main.MapFile, editor.MapFile));

            result.Add(new TwoWayBinding<string>(((GameMain)main).StartSpawnPoint, result.GetProperty<string>("StartSpawnPoint")));

            uiRenderer.Add(new ListBinding<UIComponent>(uiRenderer.Root.GetChildByName("PropertyList").Children, ui.UIElements));
            ui.Add(new ListBinding<Entity>(ui.SelectedEntities, editor.SelectedEntities));
            ui.Add(new Binding<bool>(ui.MapEditMode, editor.MapEditMode));
            ui.Add(new Binding<bool>(ui.EnablePrecision, x => !x, input.GetKey(Keys.LeftShift)));
            editor.Add(new Binding<bool>(editor.MovementEnabled, () => !ui.StringPropertyLocked && (input.MiddleMouseButton || editor.MapEditMode), ui.StringPropertyLocked, input.MiddleMouseButton, editor.MapEditMode));
            ui.Add(new TwoWayBinding<bool>(editor.NeedsSave, ui.NeedsSave));

            editor.Add(new Binding<Vector2>(editor.Movement, input.Movement));
            editor.Add(new Binding<bool>(editor.Up, input.GetKey(Keys.Space)));
            editor.Add(new Binding<bool>(editor.Down, input.GetKey(Keys.LeftControl)));
            editor.Add(new Binding<bool>(editor.Empty, input.RightMouseButton));
            editor.Add(new Binding<bool>(editor.SpeedMode, input.GetKey(Keys.LeftShift)));
            editor.Add(new Binding<bool>(editor.Extend, input.GetKey(Keys.F)));
            editor.Add(new Binding<bool>(editor.Fill, input.LeftMouseButton));
            editor.Add(new Binding<bool>(editor.EditSelection, () => input.MiddleMouseButton && editor.MapEditMode, input.MiddleMouseButton, editor.MapEditMode));

            addCommand("Delete", new PCInput.Chord { Key = Keys.X }, () => !editor.MapEditMode && editor.TransformMode.Value == Editor.TransformModes.None && editor.SelectedEntities.Count > 0, editor.DeleteSelected);
            addCommand("Duplicate", new PCInput.Chord { Modifier = Keys.LeftShift, Key = Keys.D }, () => !editor.MovementEnabled && editor.SelectedEntities.Count > 0, editor.Duplicate);

            // Start playing
            addCommand("Run", new PCInput.Chord { Modifier = Keys.LeftControl, Key = Keys.R }, () => !editor.MovementEnabled, new Command
            {
                Action = delegate()
                {
                    if (editor.NeedsSave)
                        editor.Save.Execute();
                    main.EditorEnabled.Value = false;
                    IO.MapLoader.Load(main, null, main.MapFile);
                }
            });

            result.Add(new CommandBinding(main.MapLoaded, delegate()
            {
                editor.Position.Value = Vector3.Zero;
                editor.NeedsSave.Value = false;
            }));

            addCommand("Quit", new PCInput.Chord { Modifier = Keys.LeftControl, Key = Keys.Q }, () => !editor.MovementEnabled, new Command
            {
                Action = delegate()
                {
                    throw new GameMain.ExitException();
                }
            });

            Property<bool> analyticsEnable = new Property<bool>();
            ListProperty<SessionEntry> analyticsSessions = new ListProperty<SessionEntry>();
            ListProperty<SessionEntry> analyticsActiveSessions = new ListProperty<SessionEntry>();
            ListProperty<EventEntry> analyticsEvents = new ListProperty<EventEntry>();
            ListProperty<EventEntry> analyticsActiveEvents = new ListProperty<EventEntry>();
            ListProperty<PropertyEntry> analyticsProperties = new ListProperty<PropertyEntry>();
            ListProperty<PropertyEntry> analyticsActiveProperties = new ListProperty<PropertyEntry>();
            Dictionary<Session, ModelInstance> sessionPositionModels = new Dictionary<Session, ModelInstance>();
            Dictionary<Session.EventList, List<ModelInstance>> eventPositionModels = new Dictionary<Session.EventList, List<ModelInstance>>();
            Property<bool> analyticsPlaying = new Property<bool>();
            Property<float> playbackSpeed = new Property<float> { Value = 1.0f };
            Property<float> playbackLocation = new Property<float>();

            const float timelineHeight = 32.0f;

            Scroller timelineScroller = new Scroller();
            timelineScroller.ScrollAmount.Value = 60.0f;
            timelineScroller.EnableScissor.Value = false;
            timelineScroller.DefaultScrollHorizontal.Value = true;
            timelineScroller.AnchorPoint.Value = new Vector2(0, 1);
            timelineScroller.ResizeVertical.Value = true;
            timelineScroller.Add(new Binding<Vector2, Point>(timelineScroller.Position, x => new Vector2(0, x.Y), main.ScreenSize));
            timelineScroller.Add(new Binding<Vector2, Point>(timelineScroller.Size, x => new Vector2(x.X, timelineHeight), main.ScreenSize));
            timelineScroller.Add(new Binding<bool>(timelineScroller.Visible, analyticsEnable));
            timelineScroller.Add(new Binding<bool>(timelineScroller.EnableScroll, x => !x, input.GetKey(Keys.LeftAlt)));
            uiRenderer.Root.Children.Add(timelineScroller);

            scroller.Add(new Binding<Vector2>(scroller.Size, () => new Vector2(scroller.Size.Value.X, main.ScreenSize.Value.Y - 20 - timelineScroller.ScaledSize.Value.Y), main.ScreenSize, timelineScroller.ScaledSize));

            ListContainer timelines = new ListContainer();
            timelines.Alignment.Value = ListContainer.ListAlignment.Min;
            timelines.Orientation.Value = ListContainer.ListOrientation.Vertical;
            timelines.Reversed.Value = true;
            timelineScroller.Children.Add(timelines);

            Container timeline = new Container();
            timeline.Size.Value = new Vector2(0, timelineHeight);
            timeline.Tint.Value = Microsoft.Xna.Framework.Color.Black;
            timeline.ResizeHorizontal.Value = false;
            timeline.ResizeVertical.Value = false;
            timelines.Children.Add(timeline);

            ui.PopupCommands.Add(new EditorUI.PopupCommand
            {
                Description = "Load analytics data",
                Enabled = () => editor.SelectedEntities.Count == 0 && !editor.MapEditMode && !analyticsEnable,
                Action = new Command
                {
                    Action = delegate()
                    {
                        if (main.MapFile.Value != null)
                        {
                            List<Session> sessions = ((GameMain)main).LoadAnalytics(main.MapFile);
                            if (sessions.Count > 0)
                            {
                                analyticsEnable.Value = true;
                                Dictionary<string, bool> distinctEventNames = new Dictionary<string, bool>();
                                Dictionary<string, bool> distinctPropertyNames = new Dictionary<string, bool>();
                                foreach (Session s in sessions)
                                {
                                    foreach (Session.EventList el in s.Events)
                                    {
                                        distinctEventNames[el.Name] = true;
                                        s.TotalTime = Math.Max(s.TotalTime, el.Events[el.Events.Count - 1].Time);
                                    }
                                    foreach (Session.ContinuousProperty p in s.ContinuousProperties)
                                    {
                                        if (p.Independent)
                                            distinctPropertyNames[p.Name] = true;
                                    }
                                    analyticsSessions.Add(new SessionEntry { Session = s });
                                }
                                analyticsEvents.Add(distinctEventNames.Keys.Select(x => new EventEntry { Name = x }));
                                analyticsProperties.Add(distinctPropertyNames.Keys.Select(x => new PropertyEntry { Name = x }));
                                timeline.Size.Value = new Vector2(analyticsSessions.Max(x => x.Session.TotalTime), timelineScroller.Size.Value.Y);
                                timelines.Scale.Value = new Vector2(timelineScroller.Size.Value.X / timeline.Size.Value.X, 1.0f);
                            }
                        }
                    }
                },
            });

            ListContainer sessionsSidebar = new ListContainer();
            sessionsSidebar.AnchorPoint.Value = new Vector2(1, 1);
            sessionsSidebar.Add(new Binding<Vector2>(sessionsSidebar.Position, () => new Vector2(main.ScreenSize.Value.X - 10, main.ScreenSize.Value.Y - timelineScroller.ScaledSize.Value.Y - 10), main.ScreenSize, timelineScroller.ScaledSize));
            sessionsSidebar.Add(new Binding<bool>(sessionsSidebar.Visible, analyticsEnable));
            sessionsSidebar.Alignment.Value = ListContainer.ListAlignment.Max;
            sessionsSidebar.Reversed.Value = true;
            uiRenderer.Root.Children.Add(sessionsSidebar);

            Func<string, ListContainer> createCheckboxListItem = delegate(string text)
            {
                ListContainer layout = new ListContainer();
                layout.Orientation.Value = ListContainer.ListOrientation.Horizontal;

                TextElement label = new TextElement();
                label.FontFile.Value = "Font";
                label.Text.Value = text;
                label.Name.Value = "Label";
                layout.Children.Add(label);

                Container checkboxContainer = new Container();
                checkboxContainer.PaddingBottom.Value = checkboxContainer.PaddingLeft.Value = checkboxContainer.PaddingRight.Value = checkboxContainer.PaddingTop.Value = 1.0f;
                layout.Children.Add(checkboxContainer);

                Container checkbox = new Container();
                checkbox.Name.Value = "Checkbox";
                checkbox.ResizeHorizontal.Value = checkbox.ResizeVertical.Value = false;
                checkbox.Size.Value = new Vector2(16.0f, 16.0f);
                checkboxContainer.Children.Add(checkbox);
                return layout;
            };

            Container sessionsContainer = new Container();
            sessionsContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black;
            sessionsContainer.Opacity.Value = 0.5f;
            sessionsContainer.AnchorPoint.Value = new Vector2(1, 1);
            sessionsSidebar.Children.Add(sessionsContainer);

            Scroller sessionsScroller = new Scroller();
            sessionsScroller.ResizeHorizontal.Value = true;
            sessionsScroller.ResizeVertical.Value = true;
            sessionsScroller.MaxVerticalSize.Value = 256;
            sessionsContainer.Children.Add(sessionsScroller);

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

            Property<bool> allSessions = new Property<bool>();

            sessionList.Add(new ListBinding<UIComponent, SessionEntry>(sessionList.Children, analyticsSessions, delegate(SessionEntry entry)
            {
                ListContainer item = createCheckboxListItem(entry.Session.Date.ToShortDateString() + " (" + new TimeSpan(0, 0, (int)entry.Session.TotalTime).ToString() + ")");

                Container checkbox = (Container)item.GetChildByName("Checkbox");
                checkbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(checkbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, entry.Active));

                item.Add(new CommandBinding<Point>(item.MouseLeftDown, delegate(Point p)
                {
                    if (entry.Active)
                    {
                        allSessions.Value = false;
                        analyticsActiveSessions.Remove(entry);
                    }
                    else
                        analyticsActiveSessions.Add(entry);
                }));

                return new[] { item };
            }));

            ListContainer allSessionsButton = createCheckboxListItem("[All]");
            allSessionsButton.Add(new CommandBinding<Point>(allSessionsButton.MouseLeftDown, delegate(Point p)
            {
                if (allSessions)
                {
                    allSessions.Value = false;
                    foreach (SessionEntry s in analyticsActiveSessions.ToList())
                        analyticsActiveSessions.Remove(s);
                }
                else
                {
                    allSessions.Value = true;
                    foreach (SessionEntry s in analyticsSessions)
                    {
                        if (!s.Active)
                            analyticsActiveSessions.Add(s);
                    }
                }
            }));

            Container allSessionsCheckbox = (Container)allSessionsButton.GetChildByName("Checkbox");
            allSessionsCheckbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(allSessionsCheckbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, allSessions));
            sessionList.Children.Add(allSessionsButton);

            Container eventsContainer = new Container();
            eventsContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black;
            eventsContainer.Opacity.Value = 0.5f;
            eventsContainer.AnchorPoint.Value = new Vector2(1, 1);
            sessionsSidebar.Children.Add(eventsContainer);

            Scroller eventsScroller = new Scroller();
            eventsScroller.ResizeHorizontal.Value = true;
            eventsScroller.ResizeVertical.Value = true;
            eventsScroller.MaxVerticalSize.Value = 256;
            eventsContainer.Children.Add(eventsScroller);

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

            Property<bool> allEvents = new Property<bool>();

            eventList.Add(new ListBinding<UIComponent, EventEntry>(eventList.Children, analyticsEvents, delegate(EventEntry e)
            {
                ListContainer item = createCheckboxListItem(e.Name);

                Container checkbox = (Container)item.GetChildByName("Checkbox");
                checkbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(checkbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, e.Active));

                TextElement label = (TextElement)item.GetChildByName("Label");
                label.Tint.Value = new Microsoft.Xna.Framework.Color(this.colorHash(e.Name));

                item.Add(new CommandBinding<Point>(item.MouseLeftDown, delegate(Point p)
                {
                    if (e.Active)
                    {
                        allEvents.Value = false;
                        analyticsActiveEvents.Remove(e);
                    }
                    else
                        analyticsActiveEvents.Add(e);
                }));

                return new[] { item };
            }));

            ListContainer allEventsButton = createCheckboxListItem("[All]");
            allEventsButton.Add(new CommandBinding<Point>(allEventsButton.MouseLeftDown, delegate(Point p)
            {
                if (allEvents)
                {
                    allEvents.Value = false;
                    foreach (EventEntry e in analyticsActiveEvents.ToList())
                        analyticsActiveEvents.Remove(e);
                }
                else
                {
                    allEvents.Value = true;
                    foreach (EventEntry e in analyticsEvents)
                    {
                        if (!e.Active)
                            analyticsActiveEvents.Add(e);
                    }
                }
            }));
            Container allEventsCheckbox = (Container)allEventsButton.GetChildByName("Checkbox");
            allEventsCheckbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(allEventsCheckbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, allEvents));
            eventList.Children.Add(allEventsButton);

            Container propertiesContainer = new Container();
            propertiesContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black;
            propertiesContainer.Opacity.Value = 0.5f;
            propertiesContainer.AnchorPoint.Value = new Vector2(1, 1);
            sessionsSidebar.Children.Add(propertiesContainer);

            Scroller propertiesScroller = new Scroller();
            propertiesScroller.ResizeHorizontal.Value = true;
            propertiesScroller.ResizeVertical.Value = true;
            propertiesScroller.MaxVerticalSize.Value = 256;
            propertiesContainer.Children.Add(propertiesScroller);

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

            Property<bool> allProperties = new Property<bool>();

            propertiesList.Add(new ListBinding<UIComponent, PropertyEntry>(propertiesList.Children, analyticsProperties, delegate(PropertyEntry e)
            {
                ListContainer item = createCheckboxListItem(e.Name);

                Container checkbox = (Container)item.GetChildByName("Checkbox");
                checkbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(checkbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, e.Active));

                TextElement label = (TextElement)item.GetChildByName("Label");
                label.Tint.Value = new Microsoft.Xna.Framework.Color(this.colorHash(e.Name));

                item.Add(new CommandBinding<Point>(item.MouseLeftDown, delegate(Point p)
                {
                    if (e.Active)
                    {
                        allProperties.Value = false;
                        analyticsActiveProperties.Remove(e);
                    }
                    else
                        analyticsActiveProperties.Add(e);
                }));

                return new[] { item };
            }));

            ListContainer allPropertiesButton = createCheckboxListItem("[All]");
            allPropertiesButton.Add(new CommandBinding<Point>(allPropertiesButton.MouseLeftDown, delegate(Point p)
            {
                if (allProperties)
                {
                    allProperties.Value = false;
                    foreach (PropertyEntry e in analyticsActiveProperties.ToList())
                        analyticsActiveProperties.Remove(e);
                }
                else
                {
                    allProperties.Value = true;
                    foreach (PropertyEntry e in analyticsProperties)
                    {
                        if (!e.Active)
                            analyticsActiveProperties.Add(e);
                    }
                }
            }));
            Container allPropertiesCheckbox = (Container)allPropertiesButton.GetChildByName("Checkbox");
            allPropertiesCheckbox.Add(new Binding<Microsoft.Xna.Framework.Color, bool>(allPropertiesCheckbox.Tint, x => x ? Microsoft.Xna.Framework.Color.White : Microsoft.Xna.Framework.Color.Black, allProperties));
            propertiesList.Children.Add(allPropertiesButton);

            Func<Session.EventList, LineDrawer2D> createEventLines = delegate(Session.EventList el)
            {
                LineDrawer2D line = new LineDrawer2D();
                line.Color.Value = this.colorHash(el.Name);
                line.UserData.Value = el;

                foreach (Session.Event e in el.Events)
                {
                    line.Lines.Add(new LineDrawer2D.Line
                    {
                        A = new Microsoft.Xna.Framework.Graphics.VertexPositionColor(new Vector3(e.Time, 0.0f, 0.0f), Microsoft.Xna.Framework.Color.White),
                        B = new Microsoft.Xna.Framework.Graphics.VertexPositionColor(new Vector3(e.Time, timeline.Size.Value.Y, 0.0f), Microsoft.Xna.Framework.Color.White),
                    });
                }
                return line;
            };

            analyticsActiveEvents.ItemAdded += delegate(int index, EventEntry ee)
            {
                ee.Active.Value = true;
                foreach (SessionEntry s in analyticsActiveSessions)
                {
                    Session.PositionProperty positionProperty = s.Session.PositionProperties[0];
                    foreach (Session.EventList el in s.Session.Events)
                    {
                        if (el.Name == ee.Name)
                        {
                            List<ModelInstance> models = new List<ModelInstance>();
                            Vector4 color = this.colorHash(el.Name);
                            int hash = (int)(new Color(color).PackedValue);
                            foreach (Session.Event e in el.Events)
                            {
                                ModelInstance i = new ModelInstance();
                                i.Setup("Models\\position-model", hash);
                                if (i.IsFirstInstance)
                                    i.Model.Color.Value = new Vector3(color.X, color.Y, color.Z);
                                i.Scale.Value = new Vector3(0.25f);
                                i.Transform.Value = Matrix.CreateTranslation(positionProperty[e.Time]);
                                models.Add(i);
                                result.Add(i);
                            }
                            eventPositionModels[el] = models;
                        }
                    }

                    timeline.Children.Add(s.Session.Events.Where(x => x.Name == ee.Name).Select(createEventLines));
                }
            };

            analyticsActiveEvents.ItemRemoved += delegate(int index, EventEntry e)
            {
                e.Active.Value = false;
                foreach (KeyValuePair<Session.EventList, List<ModelInstance>> pair in eventPositionModels.ToList())
                {
                    if (pair.Key.Name == e.Name)
                    {
                        foreach (ModelInstance instance in pair.Value)
                            instance.Delete.Execute();
                        eventPositionModels.Remove(pair.Key);
                    }
                }
                timeline.Children.Remove(timeline.Children.Where(x => x.UserData.Value != null && ((Session.EventList)x.UserData.Value).Name == e.Name).ToList());
            };

            analyticsActiveProperties.ItemAdded += delegate(int index, PropertyEntry e)
            {
                e.Active.Value = true;
            };

            analyticsActiveProperties.ItemRemoved += delegate(int index, PropertyEntry e)
            {
                e.Active.Value = false;
            };

            ListContainer propertyTimelines = new ListContainer();
            propertyTimelines.Alignment.Value = ListContainer.ListAlignment.Min;
            propertyTimelines.Orientation.Value = ListContainer.ListOrientation.Vertical;
            timelines.Children.Add(propertyTimelines);

            Action<LineDrawer2D> refreshPropertyGraph = delegate(LineDrawer2D lines)
            {
                string propertyName = ((PropertyEntry)lines.UserData.Value).Name;
                lines.Lines.Clear();
                float time = 0.0f, lastTime = 0.0f;
                float lastValue = 0.0f;
                bool firstLine = true;
                float max = float.MinValue, min = float.MaxValue;
                while (true)
                {
                    bool stop = true;

                    // Calculate average
                    int count = 0;
                    float sum = 0.0f;
                    foreach (SessionEntry s in analyticsActiveSessions)
                    {
                        if (time < s.Session.TotalTime)
                        {
                            Session.ContinuousProperty prop = s.Session.GetContinuousProperty(propertyName);
                            if (prop != null)
                            {
                                stop = false;
                                sum += prop[time];
                                count++;
                            }
                        }
                    }

                    if (stop)
                        break;
                    else
                    {
                        float value = sum / (float)count;
                        if (firstLine)
                            firstLine = false;
                        else
                        {
                            lines.Lines.Add(new LineDrawer2D.Line
                            {
                                A = new Microsoft.Xna.Framework.Graphics.VertexPositionColor
                                {
                                    Color = Microsoft.Xna.Framework.Color.White,
                                    Position = new Vector3(lastTime, lastValue, 0.0f),
                                },
                                B = new Microsoft.Xna.Framework.Graphics.VertexPositionColor
                                {
                                    Color = Microsoft.Xna.Framework.Color.White,
                                    Position = new Vector3(time, value, 0.0f),
                                },
                            });
                        }
                        min = Math.Min(min, value);
                        max = Math.Max(max, value);
                        lastValue = value;
                        lastTime = time;
                        time += Session.Recorder.Interval;
                    }

                    if (min < max)
                    {
                        float scale = -timelineHeight / (max - min);
                        lines.Scale.Value = new Vector2(1, scale);
                        lines.Position.Value = new Vector2(0, max * -scale);
                    }
                    else
                    {
                        lines.AnchorPoint.Value = Vector2.Zero;
                        if (min <= 0.0f)
                            lines.Position.Value = new Vector2(0, timelineHeight);
                        else
                            lines.Position.Value = new Vector2(0, timelineHeight * 0.5f);
                    }
                }
            };

            Action refreshPropertyGraphs = delegate()
            {
                foreach (LineDrawer2D line in propertyTimelines.Children.Select(x => x.Children.First()))
                    refreshPropertyGraph(line);
            };

            propertyTimelines.Add(new ListBinding<UIComponent, PropertyEntry>(propertyTimelines.Children, analyticsActiveProperties, delegate(PropertyEntry e)
            {
                Container propertyTimeline = new Container();
                propertyTimeline.Add(new Binding<Vector2>(propertyTimeline.Size, timeline.Size));
                propertyTimeline.Tint.Value = Microsoft.Xna.Framework.Color.Black;
                propertyTimeline.Opacity.Value = 0.5f;
                propertyTimeline.ResizeHorizontal.Value = false;
                propertyTimeline.ResizeVertical.Value = false;

                LineDrawer2D line = new LineDrawer2D();
                line.Color.Value = this.colorHash(e.Name);
                line.UserData.Value = e;
                propertyTimeline.Children.Add(line);

                refreshPropertyGraph(line);

                return new[] { propertyTimeline };
            }));

            analyticsActiveSessions.ItemAdded += delegate(int index, SessionEntry s)
            {
                Session.PositionProperty positionProperty = s.Session.PositionProperties[0];
                foreach (Session.EventList el in s.Session.Events)
                {
                    if (analyticsActiveEvents.FirstOrDefault(x => x.Name == el.Name) != null)
                    {
                        List<ModelInstance> models = new List<ModelInstance>();
                        Vector4 color = this.colorHash(el.Name);
                        int hash = (int)(new Color(color).PackedValue);
                        foreach (Session.Event e in el.Events)
                        {
                            ModelInstance i = new ModelInstance();
                            i.Setup("Models\\position-model", hash);
                            if (i.IsFirstInstance)
                                i.Model.Color.Value = new Vector3(color.X, color.Y, color.Z);
                            i.Scale.Value = new Vector3(0.25f);
                            i.Transform.Value = Matrix.CreateTranslation(positionProperty[e.Time]);
                            result.Add(i);
                            models.Add(i);
                        }
                        eventPositionModels[el] = models;
                    }
                }

                ModelInstance instance = new ModelInstance();
                instance.Setup("Models\\position-model", 0);
                instance.Scale.Value = new Vector3(0.25f);
                result.Add(instance);
                sessionPositionModels.Add(s.Session, instance);
                s.Active.Value = true;
                timeline.Children.Add(s.Session.Events.Where(x => analyticsActiveEvents.FirstOrDefault(y => y.Name == x.Name) != null).Select(createEventLines));
                playbackLocation.Reset();

                refreshPropertyGraphs();
            };

            analyticsActiveSessions.ItemRemoved += delegate(int index, SessionEntry s)
            {
                ModelInstance instance = sessionPositionModels[s.Session];
                instance.Delete.Execute();

                foreach (KeyValuePair<Session.EventList, List<ModelInstance>> pair in eventPositionModels.ToList())
                {
                    if (pair.Key.Session == s.Session)
                    {
                        foreach (ModelInstance i in pair.Value)
                            i.Delete.Execute();
                        eventPositionModels.Remove(pair.Key);
                    }
                }

                sessionPositionModels.Remove(s.Session);
                s.Active.Value = false;
                timeline.Children.Remove(timeline.Children.Where(x => x.UserData.Value != null && ((Session.EventList)x.UserData.Value).Session == s.Session).ToList());

                refreshPropertyGraphs();
            };

            playbackLocation.Set = delegate(float value)
            {
                if (analyticsActiveSessions.Count == 0)
                    return;

                value = Math.Max(0.0f, value);
                float end = analyticsActiveSessions.Max(x => x.Session.TotalTime);
                if (value > end)
                {
                    playbackLocation.InternalValue = end;
                    analyticsPlaying.Value = false;
                }
                else
                    playbackLocation.InternalValue = value;

                foreach (KeyValuePair<Session, ModelInstance> pair in sessionPositionModels)
                    pair.Value.Transform.Value = Matrix.CreateTranslation(pair.Key.PositionProperties[0][playbackLocation]);
            };

            LineDrawer2D playbackLine = new LineDrawer2D();
            playbackLine.Color.Value = Vector4.One;
            playbackLine.Lines.Add(new LineDrawer2D.Line
            {
                A = new Microsoft.Xna.Framework.Graphics.VertexPositionColor
                {
                    Color = Microsoft.Xna.Framework.Color.White,
                    Position = new Vector3(0.0f, -10.0f, 0.0f),
                },
                B = new Microsoft.Xna.Framework.Graphics.VertexPositionColor
                {
                    Color = Microsoft.Xna.Framework.Color.White,
                    Position = new Vector3(0.0f, timeline.Size.Value.Y, 0.0f),
                },
            });
            playbackLine.Add(new Binding<Vector2, float>(playbackLine.Position, x => new Vector2(x, 0.0f), playbackLocation));
            timeline.Children.Add(playbackLine);

            result.Add(new NotifyBinding(delegate()
            {
                allEventsButton.Detach();
                allSessionsButton.Detach();
                allPropertiesButton.Detach();
                analyticsSessions.Clear();
                analyticsEvents.Clear();
                analyticsProperties.Clear();
                eventList.Children.Add(allEventsButton);
                sessionList.Children.Add(allSessionsButton);
                propertiesList.Children.Add(allPropertiesButton);

                foreach (ModelInstance instance in sessionPositionModels.Values)
                    instance.Delete.Execute();
                sessionPositionModels.Clear();

                foreach (ModelInstance instance in eventPositionModels.Values.SelectMany(x => x))
                    instance.Delete.Execute();
                eventPositionModels.Clear();

                allEvents.Value = false;
                allSessions.Value = false;
                allProperties.Value = false;
                analyticsEnable.Value = false;

                analyticsActiveEvents.Clear();
                analyticsActiveSessions.Clear();
                analyticsActiveProperties.Clear();

                propertyTimelines.Children.Clear();

                playbackLine.Detach();
                timeline.Children.Clear();
                timeline.Children.Add(playbackLine);

                analyticsPlaying.Value = false;
                playbackLocation.Value = 0.0f;
            }, main.MapFile));

            addCommand("Toggle analytics playback", new PCInput.Chord { Modifier = Keys.LeftAlt, Key = Keys.A }, () => analyticsEnable && !editor.MovementEnabled && analyticsActiveSessions.Count > 0, new Command
            {
                Action = delegate()
                {
                    analyticsPlaying.Value = !analyticsPlaying;
                }
            });

            addCommand("Stop analytics playback", new PCInput.Chord { Key = Keys.Escape }, () => analyticsPlaying, new Command
            {
                Action = delegate()
                {
                    analyticsPlaying.Value = false;
                }
            });

            Container playbackContainer = new Container();
            playbackContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black;
            playbackContainer.Opacity.Value = 0.5f;
            sessionsSidebar.Children.Add(playbackContainer);
            playbackContainer.Add(new CommandBinding<Point, int>(playbackContainer.MouseScrolled, delegate(Point p, int delta)
            {
                playbackSpeed.Value = Math.Max(1.0f, Math.Min(10.0f, playbackSpeed.Value + delta));
            }));

            TextElement playbackLabel = new TextElement();
            playbackLabel.FontFile.Value = "Font";
            playbackLabel.Add(new Binding<string>(playbackLabel.Text, delegate()
            {
                return playbackLocation.Value.ToString("F") + " " + (analyticsPlaying ? "Playing" : "Stopped") + " " + playbackSpeed.Value.ToString("F") + "x";
            }, playbackLocation, playbackSpeed, analyticsPlaying));
            playbackContainer.Children.Add(playbackLabel);

            Container descriptionContainer = null;

            Updater timelineUpdate = new Updater
            {
                delegate(float dt)
                {
                    bool setTimelinePosition = false;

                    if (timelines.Highlighted || descriptionContainer != null)
                    {
                        if (input.LeftMouseButton)
                        {
                            setTimelinePosition = true;
                            playbackLocation.Value = Vector3.Transform(new Vector3(input.Mouse.Value.X, 0.0f, 0.0f), Matrix.Invert(timeline.GetAbsoluteTransform())).X;
                        }

                        float threshold = 3.0f / timelines.Scale.Value.X;
                        float mouseRelative = Vector3.Transform(new Vector3(input.Mouse, 0.0f), Matrix.Invert(timelines.GetAbsoluteTransform())).X;

                        if (descriptionContainer != null)
                        {
                            if (!timelines.Highlighted || (float)Math.Abs(descriptionContainer.Position.Value.X - mouseRelative) > threshold)
                            {
                                descriptionContainer.Delete.Execute();
                                descriptionContainer = null;
                            }
                        }

                        if (descriptionContainer == null && timeline.Highlighted)
                        {
                            bool stop = false;
                            foreach (UIComponent component in timeline.Children)
                            {
                                LineDrawer2D lines = component as LineDrawer2D;

                                if (lines == null)
                                    continue;

                                foreach (LineDrawer2D.Line line in lines.Lines)
                                {
                                    Session.EventList el = lines.UserData.Value as Session.EventList;
                                    if (el != null && (float)Math.Abs(line.A.Position.X - mouseRelative) < threshold)
                                    {
                                        descriptionContainer = new Container();
                                        descriptionContainer.AnchorPoint.Value = new Vector2(0.5f, 1.0f);
                                        descriptionContainer.Position.Value = new Vector2(line.A.Position.X, 0.0f);
                                        descriptionContainer.Opacity.Value = 1.0f;
                                        descriptionContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black;
                                        descriptionContainer.Add(new Binding<Vector2>(descriptionContainer.Scale, x => new Vector2(1.0f / x.X, 1.0f / x.Y), timelines.Scale));
                                        timeline.Children.Add(descriptionContainer);
                                        TextElement description = new TextElement();
                                        description.WrapWidth.Value = 256;
                                        description.Text.Value = el.Name;
                                        description.FontFile.Value = "Font";
                                        descriptionContainer.Children.Add(description);
                                        stop = true;
                                        break;
                                    }
                                }
                                if (stop)
                                    break;
                            }
                        }
                    }

                    if (analyticsPlaying && !setTimelinePosition)
                    {
                        if (analyticsActiveSessions.Count == 0)
                            analyticsPlaying.Value = false;
                        else
                            playbackLocation.Value += dt * playbackSpeed;
                    }
                }
            };
            timelineUpdate.EnabledInEditMode.Value = true;
            result.Add(timelineUpdate);

            // Save
            addCommand("Save", new PCInput.Chord { Modifier = Keys.LeftControl, Key = Keys.S }, () => !editor.MovementEnabled, editor.Save);

            // Deselect all entities
            addCommand("Deselect all", new PCInput.Chord { Modifier = Keys.LeftControl, Key = Keys.A }, () => !editor.MovementEnabled, new Command
            {
                Action = delegate()
                {
                    editor.SelectedEntities.Clear();
                }
            });

            int brush = 1;
            Action<int> changeBrush = delegate(int delta)
            {
                int foundIndex = WorldFactory.StateList.FindIndex(x => x.Name == editor.Brush);
                if (foundIndex != -1)
                    brush = foundIndex;
                int stateCount = WorldFactory.States.Count + 1;
                brush = 1 + ((brush - 1 + delta) % (stateCount - 1));
                if (brush < 1)
                    brush = stateCount + ((brush - 1) % stateCount);
                if (brush == stateCount - 1)
                    editor.Brush.Value = "[Procedural]";
                else
                    editor.Brush.Value = WorldFactory.StateList[brush].Name;
            };
            result.Add(new CommandBinding(input.GetKeyDown(Keys.Q), () => editor.MapEditMode, delegate()
            {
                changeBrush(-1);
            }));
            result.Add(new CommandBinding(input.GetKeyDown(Keys.E), () => editor.MapEditMode && !input.GetKey(Keys.LeftShift), delegate()
            {
                changeBrush(1);
            }));
            result.Add(new CommandBinding<int>(input.MouseScrolled, () => editor.MapEditMode && !input.GetKey(Keys.LeftAlt), delegate(int delta)
            {
                editor.BrushSize.Value = Math.Max(1, editor.BrushSize.Value + delta);
            }));

            addCommand("Propagate current material", new PCInput.Chord { Modifier = Keys.LeftShift, Key = Keys.E }, () => editor.MapEditMode, editor.PropagateMaterial);
            addCommand("Sample current material", new PCInput.Chord { Modifier = Keys.LeftShift, Key = Keys.Q }, () => editor.MapEditMode, editor.SampleMaterial);
            addCommand("Delete current material", new PCInput.Chord { Modifier = Keys.LeftShift, Key = Keys.X }, () => editor.MapEditMode, editor.DeleteMaterial);

            editor.Add(new Binding<Vector2>(editor.Mouse, input.Mouse, () => !input.EnableLook));

            Camera camera = main.Camera;

            Property<float> cameraDistance = new Property<float> { Value = 10.0f };
            scroller.Add(new Binding<bool>(scroller.EnableScroll, x => !x, input.GetKey(Keys.LeftAlt)));
            input.Add(new CommandBinding<int>(input.MouseScrolled, () => input.GetKey(Keys.LeftAlt), delegate(int delta)
            {
                if (timelineScroller.Highlighted && !editor.MapEditMode)
                {
                    float newScale = Math.Max(timelines.Scale.Value.X + delta * 6.0f, timelineScroller.Size.Value.X / timelines.Size.Value.X);
                    Matrix absoluteTransform = timelines.GetAbsoluteTransform();
                    float x = input.Mouse.Value.X + ((absoluteTransform.Translation.X - input.Mouse.Value.X) * (newScale / timelines.Scale.Value.X));
                    timelines.Position.Value = new Vector2(x, 0.0f);
                    timelines.Scale.Value = new Vector2(newScale, 1.0f);
                }
                else
                    cameraDistance.Value = Math.Max(5, cameraDistance.Value + delta * -2.0f);
            }));
            input.Add(new Binding<bool>(input.EnableLook, () => editor.MapEditMode || (input.MiddleMouseButton && editor.TransformMode.Value == Editor.TransformModes.None), input.MiddleMouseButton, editor.MapEditMode, editor.TransformMode));
            input.Add(new Binding<Vector3, Vector2>(camera.Angles, x => new Vector3(-x.Y, x.X, 0.0f), input.Mouse, () => input.EnableLook));
            input.Add(new Binding<bool>(main.IsMouseVisible, x => !x, input.EnableLook));
            editor.Add(new Binding<Vector3>(camera.Position, () => editor.Position.Value - (camera.Forward.Value * cameraDistance), editor.Position, input.Mouse, cameraDistance));

            PointLight editorLight = result.GetOrCreate<PointLight>("EditorLight");
            editorLight.Serialize = false;
            editorLight.Editable = false;
            editorLight.Shadowed.Value = false;
            editorLight.Add(new Binding<float>(editorLight.Attenuation, x => x * 2.0f, cameraDistance));
            editorLight.Color.Value = Vector3.One;
            editorLight.Add(new Binding<Vector3>(editorLight.Position, main.Camera.Position));
            editorLight.Enabled.Value = false;

            ui.PopupCommands.Add(new EditorUI.PopupCommand
            {
                Description = "Toggle editor light",
                Enabled = () => editor.SelectedEntities.Count == 0 && !editor.MapEditMode,
                Action = new Command { Action = () => editorLight.Enabled.Value = !editorLight.Enabled },
            });

            editor.Add(new CommandBinding(input.RightMouseButtonDown, () => !ui.PopupVisible && !editor.MapEditMode && !input.EnableLook && editor.TransformMode.Value == Editor.TransformModes.None, delegate()
            {
                // We're not editing a map
                // And we're not transforming entities
                // So we must be selecting / deselecting entities
                bool multiselect = input.GetKey(Keys.LeftShift);

                Vector2 mouse = input.Mouse;
                Microsoft.Xna.Framework.Graphics.Viewport viewport = main.GraphicsDevice.Viewport;
                Vector3 ray = Vector3.Normalize(viewport.Unproject(new Vector3(mouse.X, mouse.Y, 1), camera.Projection, camera.View, Matrix.Identity) - viewport.Unproject(new Vector3(mouse.X, mouse.Y, 0), camera.Projection, camera.View, Matrix.Identity));

                Entity closestEntity;
                Transform closestTransform;
                this.raycast(main, ray, out closestEntity, out closestTransform);

                if (closestEntity != null)
                {
                    if (editor.SelectedEntities.Count == 1 && input.GetKey(Keys.LeftControl).Value)
                    {
                        // The user is trying to connect the two entities
                        Entity entity = editor.SelectedEntities.First();
                        Command<Entity> toggleConnection = entity.GetCommand<Entity>("ToggleEntityConnected");
                        if (toggleConnection != null)
                        {
                            toggleConnection.Execute(closestEntity);
                            editor.NeedsSave.Value = true;
                        }
                        return;
                    }

                    if (multiselect)
                    {
                        if (editor.SelectedEntities.Contains(closestEntity))
                            editor.SelectedEntities.Remove(closestEntity);
                        else
                            editor.SelectedEntities.Add(closestEntity);
                    }
                    else
                    {
                        editor.SelectedEntities.Clear();
                        editor.SelectedEntities.Add(closestEntity);
                        editor.SelectedTransform.Value = closestTransform;
                    }
                }
                else
                {
                    editor.SelectedEntities.Clear();
                    editor.SelectedTransform.Value = null;
                }
            }));

            editor.Add(new CommandBinding(input.GetKeyDown(Keys.Escape), delegate()
            {
                if (editor.TransformMode.Value != Editor.TransformModes.None)
                    editor.RevertTransform.Execute();
                else if (editor.MapEditMode)
                    editor.MapEditMode.Value = false;
            }));

            addCommand("Toggle voxel edit", new PCInput.Chord { Key = Keys.Tab }, delegate()
            {
                if (editor.TransformMode.Value != Editor.TransformModes.None)
                    return false;

                if (editor.MapEditMode)
                    return true;
                else
                    return editor.SelectedEntities.Count == 1 && editor.SelectedEntities[0].Get<Map>() != null;
            },
            new Command
            {
                Action = delegate()
                {
                    editor.MapEditMode.Value = !editor.MapEditMode;
                }
            });

            addCommand
            (
                "Grab (move)",
                new PCInput.Chord { Key = Keys.G },
                () => editor.SelectedEntities.Count > 0 && !input.EnableLook && !editor.MapEditMode && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.StartTranslation
            );
            addCommand
            (
                "Grab (move)",
                new PCInput.Chord { Key = Keys.G },
                () => editor.MapEditMode && editor.VoxelSelectionActive && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.StartVoxelTranslation
            );
            addCommand
            (
                "Voxel duplicate",
                new PCInput.Chord { Key = Keys.C },
                () => editor.MapEditMode && editor.VoxelSelectionActive && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.VoxelDuplicate
            );
            addCommand
            (
                "Voxel yank",
                new PCInput.Chord { Key = Keys.Y },
                () => editor.MapEditMode && editor.VoxelSelectionActive && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.VoxelCopy
            );
            addCommand
            (
                "Voxel paste",
                new PCInput.Chord { Key = Keys.P },
                () => editor.MapEditMode && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.VoxelPaste
            );
            addCommand
            (
                "Rotate",
                new PCInput.Chord { Key = Keys.R },
                () => editor.SelectedEntities.Count > 0 && !editor.MapEditMode && !input.EnableLook && editor.TransformMode.Value == Editor.TransformModes.None,
                editor.StartRotation
            );
            addCommand
            (
                "Lock X axis",
                new PCInput.Chord { Key = Keys.X },
                () => !editor.MapEditMode && editor.TransformMode.Value != Editor.TransformModes.None,
                new Command { Action = () => editor.TransformAxis.Value = Editor.TransformAxes.X }
            );
            addCommand
            (
                "Lock Y axis",
                new PCInput.Chord { Key = Keys.Y },
                () => !editor.MapEditMode && editor.TransformMode.Value != Editor.TransformModes.None,
                new Command { Action = () => editor.TransformAxis.Value = Editor.TransformAxes.Y }
            );
            addCommand
            (
                "Lock Z axis",
                new PCInput.Chord { Key = Keys.Z },
                () => !editor.MapEditMode && editor.TransformMode.Value != Editor.TransformModes.None,
                new Command { Action = () => editor.TransformAxis.Value = Editor.TransformAxes.Z }
            );

            editor.Add(new CommandBinding
            (
                input.LeftMouseButtonDown,
                () => editor.TransformMode.Value != Editor.TransformModes.None,
                editor.CommitTransform
            ));
            editor.Add(new CommandBinding
            (
                input.RightMouseButtonDown,
                () => editor.TransformMode.Value != Editor.TransformModes.None,
                editor.RevertTransform
            ));
        }
コード例 #11
0
ファイル: PhoneNote.cs プロジェクト: dsmo7206/Lemma
		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);
			phoneUi.Root.CheckLayout();
			answerContainer.Position.Value = composeAlign.GetAbsolutePosition() + new Vector2(composeAlign.ScaledSize.Value.X, 0);
			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(null, togglePhoneMessage);
					togglePhoneMessage = null;
				}
			}, player.Note, player.SignalTower));

			entity.Add(new CommandBinding(entity.Delete, delegate()
			{
				if (togglePhoneMessage != null && togglePhoneMessage.Active)
					togglePhoneMessage.Delete.Execute();
				if (noteActive)
				{
					noteActive.Value = false;
					player.Note.Value = null;
					enableWalking.Value = true;
				}
			}));

			// 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(null, 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);
		}
コード例 #12
0
ファイル: LevitatorFactory.cs プロジェクト: kernelbitch/Lemma
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            PointLight light = result.GetOrCreate<PointLight>("PointLight");
            light.Serialize = false;

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

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

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

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

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

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

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

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

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

            const float defaultModelScale = 0.25f;
            model.Scale.Value = new Vector3(defaultModelScale);

            model.Add(new Binding<Vector3, string>(model.Color, delegate(string state)
            {
                switch (state)
                {
                    case "Alert":
                        return new Vector3(1.5f, 1.5f, 0.5f);
                    case "Chase":
                        return new Vector3(1.5f, 0.5f, 0.5f);
                    case "Levitating":
                        return new Vector3(2.0f, 1.0f, 0.5f);
                    case "Idle":
                        return new Vector3(1.0f, 1.0f, 1.0f);
                    default:
                        return new Vector3(0.0f, 0.0f, 0.0f);
                }
            }, ai.CurrentState));

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

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

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

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

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

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

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

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

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

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

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

            // Levitate

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

            const int levitateRipRadius = 4;

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

                if (!candidate.HasValue)
                    return false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            // Chase AI state

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

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

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

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

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

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

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

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

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

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

            this.SetMain(result, main);
        }
コード例 #13
0
ファイル: MapFactory.cs プロジェクト: kernelbitch/Lemma
        public void InternalBind(Entity result, Main main, bool creating = false, Transform transform = null)
        {
            if (transform == null)
                transform = result.GetOrCreate<Transform>("Transform");

            result.CannotSuspend = false;

            Map map = result.Get<Map>();

            // Apply the position and orientation components to the map
            map.Add(new TwoWayBinding<Matrix>(transform.Matrix, map.Transform));

            map.Add(new CommandBinding(map.CompletelyEmptied, delegate()
            {
                if (!main.EditorEnabled)
                    result.Delete.Execute();
            }));

            Entity world = main.Get("World").FirstOrDefault();

            map.Chunks.ItemAdded += delegate(int index, Map.Chunk chunk)
            {
                Dictionary<int, bool> models = new Dictionary<int, bool>();

                Action<Map.CellState> createModel = delegate(Map.CellState state)
                {
                    if (state.ID == 0)
                        return; // 0 = empty

                    DynamicModel<Map.MapVertex> model = new DynamicModel<Map.MapVertex>(Map.MapVertex.VertexDeclaration);
                    model.EffectFile.Value = "Effects\\Environment";
                    model.Lock = map.Lock;
                    state.ApplyTo(model);

                    /*
                    ModelAlpha debug = new ModelAlpha { Serialize = false };
                    debug.Alpha.Value = 0.01f;
                    debug.DrawOrder.Value = 11; // In front of water
                    debug.Color.Value = new Vector3(1.0f, 0.8f, 0.6f);
                    debug.Filename.Value = "Models\\alpha-box";
                    debug.CullBoundingBox.Value = false;
                    debug.DisableCulling.Value = true;
                    debug.Add(new Binding<Matrix>(debug.Transform, delegate()
                    {
                        BoundingBox box = model.BoundingBox;
                        return Matrix.CreateScale(box.Max - box.Min) * Matrix.CreateTranslation((box.Max + box.Min) * 0.5f) * transform.Matrix;
                    }, transform.Matrix, model.BoundingBox));
                    result.Add(debug);
                    */

                    model.Add(new Binding<Matrix>(model.Transform, transform.Matrix));

                    Vector3 min = new Vector3(chunk.X, chunk.Y, chunk.Z);
                    Vector3 max = min + new Vector3(map.ChunkSize);

                    model.Add(new Binding<Vector3>(model.GetVector3Parameter("Offset"), map.Offset));

                    Map.CellState s = state;

                    if (!s.ShadowCast)
                        model.UnsupportedTechniques.Add(new[] { Technique.Shadow, Technique.PointLightShadow });

                    model.Add(new ListBinding<Map.MapVertex, Map.Box>
                    (
                        model.Vertices,
                        chunk.Boxes,
                        delegate(Map.Box box)
                        {
                            Map.MapVertex[] vertices = new Map.MapVertex[box.Surfaces.Where(x => x.HasArea).Count() * 4];
                            int i = 0;
                            foreach (Map.Surface surface in box.Surfaces)
                            {
                                if (surface.HasArea)
                                {
                                    Array.Copy(surface.Vertices, 0, vertices, i, 4);
                                    i += 4;
                                }
                            }
                            return vertices;
                        },
                        x => x.Type == s
                    ));

                    result.Add(model);

                    // We have to create this binding after adding the model to the entity
                    // Because when the model loads, it automatically calculates a bounding box for it.
                    model.Add(new Binding<BoundingBox, Vector3>(model.BoundingBox, x => new BoundingBox(min - x, max - x), map.Offset));

                    models[state.ID] = true;
                };

                chunk.Boxes.ItemAdded += delegate(int i, Map.Box box)
                {
                    if ((!box.Type.Invisible || main.EditorEnabled) && !models.ContainsKey(box.Type.ID))
                        createModel(box.Type);
                };

                chunk.Boxes.ItemChanged += delegate(int i, Map.Box oldBox, Map.Box newBox)
                {
                    if ((!newBox.Type.Invisible || main.EditorEnabled) && !models.ContainsKey(newBox.Type.ID))
                        createModel(newBox.Type);
                };
            };

            this.SetMain(result, main);
            map.Offset.Changed();
        }
コード例 #14
0
ファイル: OrbFactory.cs プロジェクト: kernelbitch/Lemma
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            PointLight light = result.GetOrCreate<PointLight>("PointLight");
            light.Serialize = false;

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

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

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

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

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

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

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

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

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

            const float defaultModelScale = 0.25f;
            model.Scale.Value = new Vector3(defaultModelScale);

            model.Add(new Binding<Vector3, string>(model.Color, delegate(string state)
            {
                switch (state)
                {
                    case "Alert":
                        return new Vector3(1.5f, 1.5f, 0.5f);
                    case "Chase":
                        return new Vector3(1.5f, 0.5f, 0.5f);
                    case "Explode":
                        return new Vector3(2.0f, 1.0f, 0.5f);
                    case "Idle":
                        return new Vector3(1.0f, 1.0f, 1.0f);
                    default:
                        return new Vector3(0.0f, 0.0f, 0.0f);
                }
            }, ai.CurrentState));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                    Map.Coordinate coord = chase.Coord.Value;

                    Direction toSupport = Direction.None;

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

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

                    Direction up = toSupport.GetReverse();

                    explosionOriginalCoord.Value = coord;

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

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

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

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

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

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

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

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

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

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

                        Vector3 absolutePos = map.GetAbsolutePosition(coord);

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

            this.SetMain(result, main);
        }
コード例 #15
0
ファイル: JointFactory.cs プロジェクト: kernelbitch/Lemma
        public static void Bind(Entity result, Main main, Func<BEPUphysics.Entities.Entity, BEPUphysics.Entities.Entity, Vector3, Vector3, Vector3, ISpaceObject> createJoint, bool allowRotation, bool creating = false)
        {
            Transform mapTransform = result.GetOrCreate<Transform>("MapTransform");

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

            Factory.Get<DynamicMapFactory>().InternalBind(result, main, creating, mapTransform);

            DynamicMap map = result.Get<DynamicMap>();

            Property<Entity.Handle> parentMap = result.GetOrMakeProperty<Entity.Handle>("Parent");
            Property<Map.Coordinate> coord = result.GetOrMakeProperty<Map.Coordinate>("Coord");
            Property<Direction> dir = result.GetOrMakeProperty<Direction>("Direction", true);

            Action refreshMapTransform = delegate()
            {
                Entity parent = parentMap.Value.Target;
                if (parent != null)
                {
                    if (!parent.Active)
                        parent = null;
                    else
                    {
                        Map staticMap = parent.Get<Map>();
                        coord.Value = staticMap.GetCoordinate(transform.Position);
                        mapTransform.Position.Value = staticMap.GetAbsolutePosition(staticMap.GetRelativePosition(coord) - new Vector3(0.5f) + staticMap.Offset + map.Offset);
                        if (!allowRotation)
                            mapTransform.Orientation.Value = parent.Get<Transform>().Orientation;
                    }
                }
                else
                    mapTransform.Matrix.Value = transform.Matrix;
            };
            if (main.EditorEnabled)
                result.Add(new NotifyBinding(refreshMapTransform, transform.Matrix, map.Offset));

            ISpaceObject joint = null;
            CommandBinding jointDeleteBinding = null, physicsUpdateBinding = null;

            Action rebuildJoint = null;
            rebuildJoint = delegate()
            {
                if (joint != null)
                {
                    if (joint.Space != null)
                        main.Space.Remove(joint);
                    result.Remove(jointDeleteBinding);
                    if (physicsUpdateBinding != null)
                        result.Remove(physicsUpdateBinding);
                    physicsUpdateBinding = null;
                    joint = null;
                    jointDeleteBinding = null;
                }

                Entity parent = parentMap.Value.Target;

                if (main.EditorEnabled)
                {
                    refreshMapTransform();
                    return;
                }

                if (parent != null)
                {
                    if (!parent.Active)
                        parent = null;
                    else
                    {
                        Map staticMap = parent.Get<Map>();

                        map.PhysicsEntity.Position = mapTransform.Position;
                        if (!allowRotation)
                            map.PhysicsEntity.Orientation = mapTransform.Quaternion;

                        if (dir != Direction.None && !main.EditorEnabled)
                        {
                            Vector3 relativeLineAnchor = staticMap.GetRelativePosition(coord) - new Vector3(0.5f) + staticMap.Offset + map.Offset;
                            Vector3 lineAnchor = staticMap.GetAbsolutePosition(relativeLineAnchor);
                            DynamicMap dynamicMap = parent.Get<DynamicMap>();
                            joint = createJoint(map.PhysicsEntity, dynamicMap == null ? null : dynamicMap.PhysicsEntity, map.PhysicsEntity.Position, staticMap.GetAbsoluteVector(dir.Value.GetVector()), lineAnchor);
                            main.Space.Add(joint);
                            map.PhysicsEntity.ActivityInformation.Activate();

                            if (dynamicMap != null)
                            {
                                physicsUpdateBinding = new CommandBinding(dynamicMap.PhysicsUpdated, rebuildJoint);
                                result.Add(physicsUpdateBinding);
                            }

                            jointDeleteBinding = new CommandBinding(parent.Delete, delegate()
                            {
                                parentMap.Value = null;
                            });
                            result.Add(jointDeleteBinding);
                        }
                    }
                }
            };
            result.Add(new NotifyBinding(rebuildJoint, parentMap));
            result.Add(new CommandBinding(result.Delete, delegate()
            {
                if (joint != null && joint.Space != null)
                {
                    main.Space.Remove(joint);
                    joint = null;
                }
            }));
            result.Add(new CommandBinding(map.OnSuspended, delegate()
            {
                if (joint != null && joint.Space != null)
                    main.Space.Remove(joint);
            }));
            result.Add(new CommandBinding(map.OnResumed, delegate()
            {
                if (joint != null && joint.Space == null)
                    main.Space.Add(joint);
            }));
            rebuildJoint();
            Command rebuildJointCommand = new Command();
            result.Add(new CommandBinding(rebuildJointCommand, rebuildJoint));
            result.Add("RebuildJoint", rebuildJointCommand);

            if (main.EditorEnabled)
                JointFactory.attachEditorComponents(result, main);
        }
コード例 #16
0
ファイル: RainFactory.cs プロジェクト: kernelbitch/Lemma
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            result.CannotSuspendByDistance = true;

            const float kernelSpacing = 8.0f;
            const int kernelSize = 10;
            const float raycastHeight = 30.0f;
            const float rainStartHeight = 25.0f;
            const float raycastInterval = 0.25f;
            const float verticalSpeed = 90.0f;
            const float maxLifetime = 1.0f;

            ParticleEmitter emitter = result.Get<ParticleEmitter>("Emitter");
            emitter.Jitter.Value = new Vector3(kernelSpacing * kernelSize * 0.5f, 0.0f, kernelSpacing * kernelSize * 0.5f);
            Transform transform = result.Get<Transform>();

            Sound rainSound = result.GetOrCreate<Sound>("RainSound");
            rainSound.Cue.Value = "Rain";
            rainSound.Is3D.Value = false;
            rainSound.IsPlaying.Value = true;
            Property<float> rainSoundVolume = rainSound.GetProperty("Volume");

            Components.DirectionalLight lightning = result.GetOrCreate<Components.DirectionalLight>("Lightning");
            lightning.Enabled.Value = false;
            Vector3 originalLightningColor = lightning.Color;

            float[,] audioKernel = new float[kernelSize, kernelSize];
            float sum = 0.0f;
            for (int x = 0; x < kernelSize; x++)
            {
                for (int y = 0; y < kernelSize; y++)
                {
                    float cell = (kernelSize / 2) - new Vector2(x - (kernelSize / 2), y - (kernelSize / 2)).Length();
                    audioKernel[x, y] = cell;
                    sum += cell;
                }
            }

            for (int x = 0; x < kernelSize; x++)
            {
                for (int y = 0; y < kernelSize; y++)
                    audioKernel[x, y] /= sum;
            }

            float[,] raycastHeights = new float[kernelSize, kernelSize];

            float raycastTimer = raycastInterval;
            Vector3 kernelOffset = Vector3.Zero;
            Updater updater = new Updater
            {
                delegate(float dt)
                {
                    raycastTimer += dt;
                    if (raycastTimer > raycastInterval)
                    {
                        raycastTimer = 0.0f;
                        Vector3 cameraPos = main.Camera.Position;
                        float averageHeight = 0.0f;
                        kernelOffset = main.Camera.Position + new Vector3(kernelSize * kernelSpacing * -0.5f, raycastHeight + rainStartHeight, kernelSize * kernelSpacing * -0.5f);
                        for (int x = 0; x < kernelSize; x++)
                        {
                            for (int y = 0; y < kernelSize; y++)
                            {
                                Vector3 pos = kernelOffset + new Vector3(x * kernelSpacing, 0, y * kernelSpacing);
                                Map.GlobalRaycastResult raycast = Map.GlobalRaycast(pos, Vector3.Down, rainStartHeight + raycastHeight + (verticalSpeed * maxLifetime));
                                float height = raycast.Map == null ? float.MinValue : raycast.Position.Y;
                                raycastHeights[x, y] = height;
                                averageHeight += Math.Max(cameraPos.Y, Math.Min(height, cameraPos.Y + rainStartHeight)) * audioKernel[x, y];
                            }
                        }
                        rainSoundVolume.Value = 1.0f - ((averageHeight - cameraPos.Y) / rainStartHeight);
                    }
                }
            };
            updater.EnabledInEditMode.Value = true;
            result.Add(updater);

            Random random = new Random();
            Property<float> thunderIntervalMin = result.GetOrMakeProperty<float>("ThunderIntervalMin", true, 12.0f);
            Property<float> thunderIntervalMax = result.GetOrMakeProperty<float>("ThunderIntervalMax", true, 36.0f);
            Property<float> thunderMaxDelay = result.GetOrMakeProperty<float>("ThunderMaxDelay", true, 5.0f);
            Timer timer = result.GetOrCreate<Timer>();
            timer.Serialize = false;
            timer.Repeat.Value = true;
            timer.Interval.Value = (float)random.NextDouble() * thunderIntervalMax;
            timer.Add(new CommandBinding(timer.Command, delegate()
            {
                float volume = 0.5f + ((float)random.NextDouble() * 0.5f);
                result.Add(new Animation
                (
                    new Animation.Set<bool>(lightning.Enabled, true),
                    new Animation.Vector3MoveTo(lightning.Color, originalLightningColor * volume, 0.2f),
                    new Animation.Vector3MoveTo(lightning.Color, Vector3.Zero, 0.25f),
                    new Animation.Set<bool>(lightning.Enabled, false),
                    new Animation.Delay((1.0f - volume) * thunderMaxDelay),
                    new Animation.Execute(delegate()
                    {
                        Sound thunder = Sound.PlayCue(main, "Thunder", main.Camera.Position + Vector3.Normalize(new Vector3(2.0f * ((float)random.NextDouble() - 0.5f), 1.0f, 2.0f * ((float)random.NextDouble() - 0.5f))) * 1000.0f, volume);
                        if (thunder != null)
                            result.Add(thunder);
                    })
                ));
                timer.Interval.Value = thunderIntervalMin + ((float)random.NextDouble() * (thunderIntervalMax - thunderIntervalMin));
            }));

            if (ParticleSystem.Get(main, "Rain") == null)
            {
                ParticleSystem.Add(main, "Rain",
                new ParticleSystem.ParticleSettings
                {
                    TextureName = "Particles\\default",
                    EffectFile = "Effects\\ParticleRain",
                    MaxParticles = 25000,
                    Duration = TimeSpan.FromSeconds(maxLifetime),
                    MinHorizontalVelocity = 0.0f,
                    MaxHorizontalVelocity = 0.0f,
                    MinVerticalVelocity = -verticalSpeed,
                    MaxVerticalVelocity = -verticalSpeed,
                    Gravity = new Vector3(0.0f, 0.0f, 0.0f),
                    MinRotateSpeed = 0.0f,
                    MaxRotateSpeed = 0.0f,
                    MinStartSize = 0.3f,
                    MaxStartSize = 0.3f,
                    MinEndSize = 0.3f,
                    MaxEndSize = 0.3f,
                    BlendState = BlendState.Opaque,
                    MinColor = new Vector4(0.5f, 0.6f, 0.7f, 1.0f),
                    MaxColor = new Vector4(0.5f, 0.6f, 0.7f, 1.0f),
                });
                emitter.ParticleType.Reset();
            }

            emitter.AddParticle = delegate(Vector3 position, Vector3 velocity)
            {
                Vector3 kernelCoord = (position - kernelOffset) / kernelSpacing;
                float height = raycastHeights[Math.Max(0, Math.Min(kernelSize - 1, (int)kernelCoord.X)), Math.Max(0, Math.Min(kernelSize - 1, (int)kernelCoord.Z))];
                if (height < position.Y)
                    emitter.ParticleSystem.AddParticle(position, Vector3.Zero, Math.Min((position.Y - height) / verticalSpeed, maxLifetime));
            };

            emitter.Add(new Binding<Vector3>(emitter.Position, x => x + new Vector3(0.0f, rainStartHeight, 0.0f), main.Camera.Position));

            this.SetMain(result, main);
        }
コード例 #17
0
ファイル: PlayerUI.cs プロジェクト: dsmo7206/Lemma
		public static void Attach(Main main, Entity entity, UIRenderer ui, Property<float> health, Property<float> rotation, Property<bool> noteActive, Property<bool> phoneActive, Property<Vector3> linearVelocity, Property<bool> enableDebugVelocity)
		{
			Sprite damageOverlay = new Sprite();
			damageOverlay.Image.Value = "Images\\damage";
			damageOverlay.AnchorPoint.Value = new Vector2(0.5f);
			ui.Root.Children.Add(damageOverlay);

			// Center the damage overlay and scale it to fit the screen
			damageOverlay.Add(new Binding<Vector2, Point>(damageOverlay.Position, x => new Vector2(x.X * 0.5f, x.Y * 0.5f), main.ScreenSize));
			damageOverlay.Add(new Binding<Vector2>(damageOverlay.Scale, () => new Vector2(main.ScreenSize.Value.X / damageOverlay.Size.Value.X, main.ScreenSize.Value.Y / damageOverlay.Size.Value.Y), main.ScreenSize, damageOverlay.Size));
			damageOverlay.Add(new Binding<float, float>(damageOverlay.Opacity, x => 1.0f - x, health));

			Container debugVelocityContainer = main.UIFactory.CreateContainer();
			debugVelocityContainer.Opacity.Value = UIFactory.Opacity;
			debugVelocityContainer.AnchorPoint.Value = new Vector2(1.0f, 0.0f);
			bool vr = false;
#if VR
			vr = main.VR;
#endif
			debugVelocityContainer.Add(new Binding<Vector2, Point>(debugVelocityContainer.Position, x => new Vector2(x.X * 0.9f, x.Y * (vr ? 0.7f : 0.9f)), main.ScreenSize));
			debugVelocityContainer.Add(new Binding<bool>(debugVelocityContainer.Visible, enableDebugVelocity));
			ui.Root.Children.Add(debugVelocityContainer);

			ListContainer debugVelocityList = new ListContainer();
			debugVelocityList.Orientation.Value = ListContainer.ListOrientation.Vertical;
			debugVelocityContainer.Children.Add(debugVelocityList);

			TextElement debugVelocity = main.UIFactory.CreateLabel();
			debugVelocity.Add(new Binding<string, Vector3>(debugVelocity.Text, x => Math.Abs(x.Y).ToString("00.00"), linearVelocity));
			debugVelocityList.Children.Add(debugVelocity);

			TextElement debugHorizontalVelocity = main.UIFactory.CreateLabel();
			debugHorizontalVelocity.Add(new Binding<string, Vector3>(debugHorizontalVelocity.Text, x =>
			{
				x.Y = 0.0f;
				return x.Length().ToString("00.00");
			}, linearVelocity));
			debugVelocityList.Children.Add(debugHorizontalVelocity);

#if VR
			if (main.VR)
			{
				VirtualReticle reticleController = entity.GetOrCreate<VirtualReticle>();
				reticleController.Add(new Binding<float>(reticleController.Rotation, rotation));

				ModelNonPostProcessed reticle = entity.Create<ModelNonPostProcessed>();
				reticle.Filename.Value = "Models\\plane";
				reticle.EffectFile.Value = "Effects\\VirtualUI";
				reticle.DiffuseTexture.Value = "Images\\reticle";
				reticle.Add(new Binding<Matrix>(reticle.Transform, reticleController.Transform));
				reticle.Add(new Binding<bool>(reticle.Enabled, () => !main.Paused && !phoneActive && !noteActive && main.Settings.EnableReticleVR, main.Paused, phoneActive, noteActive, main.Settings.EnableReticleVR));
			}
			else
#endif
			{
				Sprite reticle = new Sprite();
				reticle.Image.Value = "Images\\reticle";
				reticle.AnchorPoint.Value = new Vector2(0.5f);
				reticle.Opacity.Value = 0.5f;
				ui.Root.Children.Add(reticle);

				reticle.Add(new Binding<bool>(reticle.Visible, main.Settings.EnableReticle));

				// Center the reticle
				reticle.Add(new Binding<Vector2, Point>(reticle.Position, x => new Vector2(x.X * 0.5f, x.Y * 0.5f), main.ScreenSize));
			}

			UIComponent targets = new UIComponent();
			ui.Root.Children.Add(targets);

			TargetUI targetUi = entity.GetOrCreate<TargetUI>();
			targetUi.Add(new ListBinding<UIComponent>(targetUi.Sprites, targets.Children));

			targets.Add(new ListBinding<UIComponent, Transform>(targets.Children, TargetFactory.Positions, delegate(Transform target)
			{
				Sprite sprite = new Sprite();
				sprite.Image.Value = "Images\\target";
				sprite.AnchorPoint.Value = new Vector2(0.5f, 0.5f);
				sprite.UserData.Value = target;
				sprite.Add(new Binding<bool>(sprite.Visible, () => target.Enabled && main.Settings.EnableWaypoints, target.Enabled, main.Settings.EnableWaypoints));
				return sprite;
			}));
		}
コード例 #18
0
        private static Entity CreateTrackingEntity(EntityAsset entityAsset, Entity rootEntityAsset, ModelAsset modelAsset, string nodeName)
        {
            var childEntity = new Entity { Name = nodeName };

            // Add TransformComponent
            childEntity.Add(TransformComponent.Key, new TransformComponent());

            // Add ModelNodeLinkComponent
            childEntity.Add(ModelNodeLinkComponent.Key, new ModelNodeLinkComponent
            {
                NodeName = nodeName,
                Target = rootEntityAsset.Get(ModelComponent.Key),
            });

            // Add this asset to the list
            entityAsset.Hierarchy.Entities.Add(childEntity);

            // Get or create transformation component
            var transformationComponent = rootEntityAsset.GetOrCreate(TransformComponent.Key);

            // Mark node as preserved
            modelAsset.PreserveNodes(new List<string> { nodeName });

            // Add as children of model entity
            transformationComponent.Children.Add(childEntity.GetOrCreate(TransformComponent.Key));

            return childEntity;
        }
コード例 #19
0
ファイル: SnakeFactory.cs プロジェクト: kernelbitch/Lemma
        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);
        }
コード例 #20
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            Transform transform = result.Get<Transform>();
            EnemyBase enemy = result.GetOrCreate<EnemyBase>("Base");
            PlayerCylinderTrigger trigger = result.Get<PlayerCylinderTrigger>();

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

            ListProperty<Entity.Handle> dynamicMaps = result.GetListProperty<Entity.Handle>("DynamicMaps");
            Property<float> timeUntilRebuild = result.GetProperty<float>("TimeUntilRebuild");
            Property<float> timeUntilRebuildComplete = result.GetProperty<float>("TimeUntilRebuildComplete");
            Property<float> rebuildDelay = result.GetProperty<float>("RebuildDelay");
            Property<float> rebuildTime = result.GetProperty<float>("RebuildTime");

            const float rebuildTimeMultiplier = 0.03f;

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

            trigger.Add(new Binding<Matrix>(trigger.Transform, () => Matrix.CreateTranslation(0.0f, 0.0f, enemy.Offset) * transform.Matrix, transform.Matrix, enemy.Offset));

            Action<Entity> fall = delegate(Entity player)
            {
                if (timeUntilRebuild.Value > 0 || timeUntilRebuildComplete.Value > 0)
                    return;

                if (!enemy.IsValid)
                {
                    result.Delete.Execute();
                    return;
                }

                // Disable the cell-emptied notification.
                // This way, we won't think that the base has been destroyed by the player.
                // We are not in fact dying, we're just destroying the base so we can fall over.
                enemy.EnableCellEmptyBinding = false;

                Map m = enemy.Map.Value.Target.Get<Map>();

                m.Empty(enemy.BaseBoxes.SelectMany(x => x.GetCoords()));

                m.Regenerate(delegate(List<DynamicMap> spawnedMaps)
                {
                    Vector3 playerPos = player.Get<Transform>().Position;
                    playerPos += player.Get<Player>().LinearVelocity.Value * 0.65f;
                    foreach (DynamicMap newMap in spawnedMaps)
                    {
                        Vector3 toPlayer = playerPos - newMap.PhysicsEntity.Position;
                        toPlayer.Normalize();
                        if (Math.Abs(toPlayer.Y) < 0.9f)
                        {
                            toPlayer *= 25.0f * newMap.PhysicsEntity.Mass;

                            Vector3 positionAtPlayerHeight = newMap.PhysicsEntity.Position;
                            Vector3 impulseAtBase = toPlayer * -0.75f;
                            impulseAtBase.Y = 0.0f;
                            positionAtPlayerHeight.Y = playerPos.Y;
                            newMap.PhysicsEntity.ApplyImpulse(ref positionAtPlayerHeight, ref impulseAtBase);

                            newMap.PhysicsEntity.ApplyLinearImpulse(ref toPlayer);
                        }
                        newMap.PhysicsEntity.Material.KineticFriction = 1.0f;
                        newMap.PhysicsEntity.Material.StaticFriction = 1.0f;
                        dynamicMaps.Add(newMap.Entity);
                    }
                });

                timeUntilRebuild.Value = rebuildDelay;
            };

            result.Add(new PostInitialization
            {
                delegate()
                {
                    foreach (Entity.Handle map in dynamicMaps)
                    {
                        if (map.Target != null)
                        {
                            BEPUphysics.Entities.MorphableEntity e = map.Target.Get<DynamicMap>().PhysicsEntity;
                            e.Material.KineticFriction = 1.0f;
                            e.Material.StaticFriction = 1.0f;
                        }
                    }
                }
            });

            result.Add(new CommandBinding<Entity>(trigger.PlayerEntered, fall));

            result.Add(new Updater
            {
                delegate(float dt)
                {
                    if (timeUntilRebuild > 0)
                    {
                        if (enemy.Map.Value.Target == null || !enemy.Map.Value.Target.Active)
                        {
                            result.Delete.Execute();
                            return;
                        }

                        float newValue = Math.Max(0.0f, timeUntilRebuild.Value - dt);
                        timeUntilRebuild.Value = newValue;
                        if (newValue == 0.0f)
                        {
                            // Rebuild
                            Map m = enemy.Map.Value.Target.Get<Map>();

                            int index = 0;

                            Vector3 baseCenter = Vector3.Zero;

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

                            Entity targetMap = enemy.Map.Value.Target;

                            foreach (Map.Coordinate c in enemy.BaseBoxes.SelectMany(x => x.GetCoords()))
                            {
                                if (m[c].ID == 0)
                                {
                                    Entity block = factory.CreateAndBind(main);
                                    c.Data.ApplyToEffectBlock(block.Get<ModelInstance>());
                                    block.GetProperty<Vector3>("Offset").Value = m.GetRelativePosition(c);
                                    block.GetProperty<Vector3>("StartPosition").Value = m.GetAbsolutePosition(c) + new Vector3(0.25f, 0.5f, 0.25f) * index;
                                    block.GetProperty<Matrix>("StartOrientation").Value = Matrix.CreateRotationX(0.15f * index) * Matrix.CreateRotationY(0.15f * index);
                                    block.GetProperty<float>("TotalLifetime").Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime);
                                    factory.Setup(block, targetMap, c, c.Data.ID);
                                    main.Add(block);
                                    index++;
                                    baseCenter += new Vector3(c.X, c.Y, c.Z);
                                }
                            }

                            baseCenter /= index; // Get the average position of the base cells

                            foreach (Entity.Handle e in dynamicMaps)
                            {
                                Entity dynamicMap = e.Target;
                                Map dynamicMapComponent = dynamicMap != null && dynamicMap.Active ? dynamicMap.Get<Map>() : null;

                                if (dynamicMap == null || !dynamicMap.Active)
                                    continue;

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

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

                                foreach (Map.Coordinate c in dynamicMapComponent.Chunks.SelectMany(x => x.Boxes).SelectMany(x => x.GetCoords()))
                                {
                                    if (m[c].ID == 0)
                                        coords.Add(c);
                                }

                                foreach (Map.Coordinate c in coords.OrderBy(x => (new Vector3(x.X, x.Y, x.Z) - baseCenter).LengthSquared()))
                                {
                                    Entity block = factory.CreateAndBind(main);
                                    c.Data.ApplyToEffectBlock(block.Get<ModelInstance>());
                                    block.GetProperty<Vector3>("Offset").Value = m.GetRelativePosition(c);
                                    block.GetProperty<bool>("Scale").Value = dynamicMapComponent == null;
                                    if (dynamicMapComponent != null && dynamicMapComponent[c].ID == c.Data.ID)
                                    {
                                        block.GetProperty<Vector3>("StartPosition").Value = dynamicMapComponent.GetAbsolutePosition(c);
                                        block.GetProperty<Matrix>("StartOrientation").Value = orientation;
                                    }
                                    else
                                    {
                                        block.GetProperty<Vector3>("StartPosition").Value = m.GetAbsolutePosition(c) + new Vector3(0.25f, 0.5f, 0.25f) * index;
                                        block.GetProperty<Matrix>("StartOrientation").Value = Matrix.CreateRotationX(0.15f * index) * Matrix.CreateRotationY(0.15f * index);
                                    }
                                    block.GetProperty<float>("TotalLifetime").Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime);
                                    factory.Setup(block, targetMap, c, c.Data.ID);
                                    main.Add(block);
                                    index++;
                                }
                                dynamicMap.Delete.Execute();
                            }
                            timeUntilRebuildComplete.Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime);
                            dynamicMaps.Clear();
                        }
                    }
                    else if (timeUntilRebuildComplete > 0)
                    {
                        // Rebuilding
                        float newValue = Math.Max(0.0f, timeUntilRebuildComplete.Value - dt);
                        timeUntilRebuildComplete.Value = newValue;
                        if (newValue == 0.0f)
                        {
                            // Done rebuilding
                            if (!enemy.IsValid)
                                result.Delete.Execute();
                            else
                            {
                                enemy.EnableCellEmptyBinding = !main.EditorEnabled;
                                if (trigger.IsTriggered)
                                    fall(trigger.Player.Value.Target);
                            }
                        }
                    }
                }
            });

            this.SetMain(result, main);
        }
コード例 #21
0
ファイル: PhoneNote.cs プロジェクト: sparker/Lemma
		public static void Attach(Main main, Entity result, AnimatedModel model, FPSInput input, Phone phone, Property<bool> enableWalking, Property<bool> enableMoves)
		{
			Property<bool> phoneActive = result.GetOrMakeProperty<bool>("PhoneActive");
			Property<bool> noteActive = result.GetOrMakeProperty<bool>("NoteActive");

			UIRenderer phoneUi = result.GetOrCreate<UIRenderer>("PhoneUI");

			Property<Entity.Handle> signalTower = result.GetOrMakeProperty<Entity.Handle>("SignalTower");

			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;

			Model phoneModel = result.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 = result.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 = result.GetOrCreate<PointLight>("PhoneLight");
			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));

			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<Color, string, float, Container> makeButton = delegate(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;
				Color highlightColor = new Color(color.ToVector4() + new Vector4(0.2f, 0.2f, 0.2f, 0.0f));
				bg.Add(new Binding<Color, bool>(bg.Tint, x => x ? highlightColor : color, bg.Highlighted));

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

			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;
			};

			Color incomingColor = new Color(0.0f, 0.0f, 0.0f, 1.0f);
			Color outgoingColor = new Color(0.0f, 0.175f, 0.35f, 1.0f);

			Container topBarContainer = new Container();
			topBarContainer.ResizeHorizontal.Value = false;
			topBarContainer.Size.Value = new Vector2(phoneUi.RenderTargetSize.Value.X, 0.0f);
			topBarContainer.Tint.Value = new Color(0.15f, 0.15f, 0.15f, 1.0f);
			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 = "Font";
			noService.Text.Value = "\\no service";
			phoneTopBar.Children.Add(noService);

			signalIcon.Add(new Binding<bool, Entity.Handle>(signalIcon.Visible, x => x.Target != null && x.Target.Active, signalTower));
			noService.Add(new Binding<bool, Entity.Handle>(noService.Visible, x => x.Target == null || !x.Target.Active, signalTower));

			ListContainer phoneLayout = new ListContainer();
			phoneLayout.Spacing.Value = padding;
			phoneLayout.Orientation.Value = ListContainer.ListOrientation.Vertical;
			phoneLayout.Add(new Binding<Vector2>(phoneLayout.Position, x => new Vector2(padding, x.Y), topBarContainer.Size));
			phoneLayout.Add(new Binding<Vector2>(phoneLayout.Size, () => new Vector2(phoneUi.RenderTargetSize.Value.X - padding * 2.0f, phoneUi.RenderTargetSize.Value.Y - padding - topBarContainer.Size.Value.Y), phoneUi.RenderTargetSize, topBarContainer.Size));
			phoneUi.Root.Children.Add(phoneLayout);

			Container composeButton = makeButton(new Color(0.5f, 0.0f, 0.0f, 1.0f), "\\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(phoneLayout.Size.Value.X, phoneLayout.Size.Value.Y - phoneLayout.Spacing.Value - composeAlign.ScaledSize.Value.Y), phoneLayout.Size, phoneLayout.Spacing, composeAlign.ScaledSize));

			phoneLayout.Children.Add(phoneScroll);
			phoneLayout.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.Position.Value + new Vector2(composeAlign.ScaledSize.Value.X + padding, padding * 3.0f), 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<Point>(composeButton.MouseLeftUp, delegate(Point p)
			{
				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;
				}
			}));

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

			// Note

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

			const float noteWidth = 400.0f;
			const float noteScale = 0.0009f;

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

			Model noteModel = result.GetOrCreate<Model>("Note");
			noteModel.Filename.Value = "Models\\plane";
			noteModel.EffectFile.Value = "Effects\\Default";
			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.Scale.Value = new Vector3(1.0f, (float)noteUi.RenderTargetSize.Value.Y * noteScale, (float)noteUi.RenderTargetSize.Value.X * noteScale);
			noteModel.Serialize = false;
			noteModel.Enabled.Value = false;
			Property<Entity.Handle> note = result.GetOrMakeProperty<Entity.Handle>("Note");

			Container togglePhoneMessage = null;

			result.Add(new NotifyBinding(delegate()
			{
				bool hasNoteOrSignalTower = (note.Value.Target != null && note.Value.Target.Active)
					|| (signalTower.Value.Target != null && signalTower.Value.Target.Active);

				if (togglePhoneMessage == null && hasNoteOrSignalTower)
					togglePhoneMessage = ((GameMain)main).Menu.ShowMessage(result, "[{{TogglePhone}}]");
				else if (togglePhoneMessage != null && !hasNoteOrSignalTower && !phoneActive && !noteActive)
				{
					((GameMain)main).Menu.HideMessage(result, togglePhoneMessage);
					togglePhoneMessage = null;
				}
			}, note, signalTower));

			result.Add(new CommandBinding(result.Delete, delegate()
			{
				((GameMain)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 = "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

			Action<bool> showNote = delegate(bool show)
			{
				noteActive.Value = show;
				input.EnableLook.Value = input.EnableMouse.Value = !noteActive;
				main.IsMouseVisible.Value = false;
				enableWalking.Value = enableMoves.Value = !noteActive;
				noteModel.Enabled.Value = noteActive;
				noteUi.Enabled.Value = noteActive;

				model.Stop("Phone", "Note");
				Entity noteEntity = note.Value.Target;
				if (noteEntity != null && noteEntity.Active)
				{
					if (noteActive)
					{
						noteUiImage.Image.Value = noteEntity.GetOrMakeProperty<string>("Image");
						noteUiText.Text.Value = noteEntity.GetOrMakeProperty<string>("Text");
						model.StartClip("Note", 6, true, AnimatedModel.DefaultBlendTime * 2.0f);
						float startRotationY = input.Mouse.Value.Y;
						// Level the player's view
						result.Add(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.Type.OutQuadratic
							)
						));
					}
					else
					{
						Property<bool> collected = noteEntity.GetOrMakeProperty<bool>("Collected");
						if (!collected)
							collected.Value = true;
					}
				}
			};

			// Toggle phone

			Container phoneTutorialMessage = null;

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

				if (phoneTutorialMessage != null)
				{
					((GameMain)main).Menu.HideMessage(result, phoneTutorialMessage);
					phoneTutorialMessage = null;
				}

				if (show || (phone.Schedules.Count == 0 && !phone.WaitForAnswer))
				{
					phoneActive.Value = show;
					input.EnableLook.Value = input.EnableMouse.Value = !phoneActive;
					main.IsMouseVisible.Value = phoneActive;
					enableWalking.Value = enableMoves.Value = !phoneActive;
					phoneModel.Enabled.Value = phoneActive;
					screen.Enabled.Value = phoneActive;
					phoneUi.Enabled.Value = phoneActive;
					phoneLight.Enabled.Value = phoneActive;
					answerContainer.Visible.Value = false;

					model.Stop("Phone", "Note");
					if (phoneActive)
					{
						if (!phone.TutorialShown)
						{
							phone.TutorialShown.Value = true;
							phoneTutorialMessage = ((GameMain)main).Menu.ShowMessage(result, "\\scroll for more");
						}
						phoneScroll.CheckLayout();
						scrollToBottom();

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

						// Level the player's view
						float startRotationY = input.Mouse.Value.Y;
						result.Add(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.Type.OutQuadratic
							)
						));
					}
				}
			};

			input.Bind(((GameMain)main).Settings.TogglePhone, PCInput.InputState.Up, delegate()
			{
				if (noteActive || phoneActive || phone.CanReceiveMessages)
				{
					if (!phoneActive && (noteActive || note.Value.Target != null && note.Value.Target.Active))
						showNote(!noteActive);
					else if (phone.Enabled)
						showPhone(!phoneActive);
				}
			});

			// Gamepad code for the phone

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

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

			Action<int> scrollPhone = delegate(int delta)
			{
				if (answerContainer.Visible)
				{
					answerList.Children[selectedAnswer].Highlighted.Value = false;
					selectedAnswer += delta;
					while (selectedAnswer < 0)
						selectedAnswer += answerList.Children.Count;
					while (selectedAnswer > answerList.Children.Count - 1)
						selectedAnswer -= answerList.Children.Count;
					answerList.Children[selectedAnswer].Highlighted.Value = true;
				}
				else
					phoneScroll.MouseScrolled.Execute(new Point(), delta * -4);
			};

			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);
			}));

			msgList.Add(new ListBinding<UIComponent, Phone.Message>
			(
				msgList.Children,
				phone.Messages,
				delegate(Phone.Message msg)
				{
					return makeAlign(makeButton(msg.Incoming ? incomingColor : outgoingColor, "\\" + (msg.Text == null ? msg.ID : msg.Text), messageWidth - padding * 2.0f), !msg.Incoming);
				}
			));

			answerList.Add(new ListBinding<UIComponent, Phone.Ans>
			(
				answerList.Children,
				phone.ActiveAnswers,
				delegate(Phone.Ans answer)
				{
					UIComponent button = makeButton(outgoingColor, "\\" + (answer.Text == null ? answer.ID : answer.Text), messageWidth - padding * 4.0f);
					button.Add(new CommandBinding<Point>(button.MouseLeftUp, delegate(Point p)
					{
						phone.Answer(answer);
						
						// Disable the signal tower
						Entity s = signalTower.Value.Target;
						if (s != null && s.Active)
							s.Get<SignalTower>().Initial.Value = null;

						scrollToBottom();
						if (togglePhoneMessage == null && phone.Schedules.Count == 0) // No more messages incoming
							togglePhoneMessage = ((GameMain)main).Menu.ShowMessage(result, "[{{TogglePhone}}]");
					}));
					return button;
				}
			));

			Action refreshComposeButtonVisibility = delegate()
			{
				bool show = phone.ActiveAnswers.Count > 0 && phone.Schedules.Count == 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();

			result.Add(new CommandBinding(phone.MessageReceived, delegate()
			{
				if (phoneActive)
					scrollToBottom();
				else
					showPhone(true);
				
				// Animate the new message
				Container lastMessage = (Container)msgList.Children[msgList.Children.Count - 1].Children[0];
				lastMessage.CheckLayout();
				Vector2 originalSize = lastMessage.Size;
				lastMessage.Size.Value = new Vector2(0, originalSize.Y);
				main.AddComponent(new Animation
				(
					new Animation.Ease(new Animation.Vector2MoveTo(lastMessage.Size, originalSize, 0.5f), Animation.Ease.Type.OutExponential)
				));

				AkSoundEngine.PostEvent("Phone_Play", result);
				if (togglePhoneMessage == null && phone.Schedules.Count == 0 && phone.ActiveAnswers.Count == 0) // No more messages incoming, and no more answers to give
					togglePhoneMessage = ((GameMain)main).Menu.ShowMessage(result, "[{{TogglePhone}}]");
			}));

			if (noteActive)
				showNote(true);
			else if (phoneActive)
				showPhone(true);
		}