protected static void hideMessage(Container container, float delay = 0.0f) { if (container != null && container.Active) { WorldFactory.Get().Add(new Animation ( new Animation.Delay(messageFadeTime + delay), new Animation.Parallel ( new Animation.FloatMoveTo(container.Opacity, 0.0f, messageFadeTime), new Animation.FloatMoveTo(((TextElement)container.Children[0]).Opacity, 0.0f, messageFadeTime) ), new Animation.Execute(container.Delete) )); } }
public static void Transition(Main main, string nextMap, string spawn = null) { Container loadingNotification = new Container(); loadingNotification.Tint.Value = Microsoft.Xna.Framework.Color.Black; loadingNotification.Opacity.Value = UIFactory.Opacity; TextElement loadingNotificationText = new TextElement(); loadingNotificationText.Name.Value = "Text"; loadingNotificationText.FontFile.Value = main.Font; loadingNotificationText.Text.Value = "\\loading"; loadingNotification.Children.Add(loadingNotificationText); Animation anim = new Animation ( new Animation.Set<bool>(main.Menu.CanPause, false), main.Spawner.FlashAnimation(), new Animation.Execute(delegate() { main.UI.Root.GetChildByName("Notifications").Children.Add(loadingNotification); }), new Animation.Delay(0.01f), new Animation.Execute(delegate() { #if DEMO if (nextMap == "forest") { main.Spawner.StartSpawnPoint.Value = "demo"; MapLoader.Load(main, Main.MenuMap); } else #endif { // We are exiting the map; just save the state of the map without the player. ListProperty<RespawnLocation> respawnLocations = PlayerDataFactory.Instance.Get<PlayerData>().RespawnLocations; respawnLocations.Clear(); List<Entity> persistentEntities = main.Entities.Where((Func<Entity, bool>)MapLoader.entityIsPersistent).ToList(); Stream stream = new MemoryStream(); IO.MapLoader.Serializer.Serialize(stream, persistentEntities); foreach (Entity e in persistentEntities) e.Delete.Execute(); main.Spawner.StartSpawnPoint.Value = spawn; if (PlayerFactory.Instance != null) PlayerFactory.Instance.Delete.Execute(); main.SaveCurrentMap(null, default(Point)); MapLoader.Load(main, nextMap); stream.Seek(0, SeekOrigin.Begin); List<Entity> entities = (List<Entity>)IO.MapLoader.Serializer.Deserialize(stream); foreach (Entity e in entities) { Factory<Main> factory = Factory<Main>.Get(e.Type); e.GUID = 0; factory.Bind(e, main); main.Add(e); } stream.Dispose(); } }), new Animation.Delay(0.01f), new Animation.Execute(loadingNotification.Delete), new Animation.Set<bool>(main.Menu.CanPause, true), new Animation.Execute(main.ScheduleSave) ); anim.EnabledWhenPaused = false; main.AddComponent(anim); }
public override void Bind(Entity result, Main main, bool creating = false) { this.SetMain(result, main); Transform transform = result.Get<Transform>(); PlayerTrigger trigger = result.Get<PlayerTrigger>(); Property<string> nextMap = result.GetProperty<string>("NextMap"); Property<string> startSpawnPoint = result.GetProperty<string>("SpawnPoint"); trigger.Add(new TwoWayBinding<Vector3>(transform.Position, trigger.Position)); trigger.Add(new CommandBinding<Entity>(trigger.PlayerEntered, delegate(Entity player) { XmlSerializer serializer = new XmlSerializer(typeof(List<Entity>)); Container notification = new Container(); notification.Tint.Value = Microsoft.Xna.Framework.Color.Black; notification.Opacity.Value = 0.5f; TextElement notificationText = new TextElement(); notificationText.Name.Value = "Text"; notificationText.FontFile.Value = "Font"; notificationText.Text.Value = "Loading..."; notification.Children.Add(notificationText); ((GameMain)main).UI.Root.GetChildByName("Notifications").Children.Add(notification); Stream stream = new MemoryStream(); main.AddComponent(new Animation ( new Animation.Delay(0.01f), new Animation.Execute(delegate() { // We are exiting the map; just save the state of the map without the player. ListProperty<PlayerFactory.RespawnLocation> respawnLocations = Factory.Get<PlayerDataFactory>().Instance(main).GetOrMakeListProperty<PlayerFactory.RespawnLocation>("RespawnLocations"); respawnLocations.Clear(); List<Entity> persistentEntities = main.Entities.Where((Func<Entity, bool>)MapExitFactory.isPersistent).ToList(); serializer.Serialize(stream, persistentEntities); foreach (Entity e in persistentEntities) e.Delete.Execute(); ((GameMain)main).StartSpawnPoint = startSpawnPoint; }), new Animation.Execute(((GameMain)main).SaveCurrentMap), new Animation.Set<string>(main.MapFile, nextMap), new Animation.Execute(delegate() { notification.Visible.Value = false; stream.Seek(0, SeekOrigin.Begin); List<Entity> entities = (List<Entity>)serializer.Deserialize(stream); foreach (Entity entity in entities) { Factory factory = Factory.Get(entity.Type); factory.Bind(entity, main); main.Add(entity); } stream.Dispose(); }), new Animation.Delay(1.5f), new Animation.Set<string>(notificationText.Text, "Saving..."), new Animation.Set<bool>(notification.Visible, true), new Animation.Delay(0.01f), new Animation.Execute(((GameMain)main).Save), new Animation.Set<string>(notificationText.Text, "Saved"), new Animation.Parallel ( new Animation.FloatMoveTo(notification.Opacity, 0.0f, 1.0f), new Animation.FloatMoveTo(notificationText.Opacity, 0.0f, 1.0f) ), new Animation.Execute(notification.Delete) )); })); }
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 )); }
public override Entity Create(Main main) { Entity result = new Entity(main, "Editor"); result.Serialize = false; Editor editor = new Editor(); EditorUI ui = new EditorUI { Editable = false }; Model model = new Model { Editable = false, Filename = new Property<string> { Value = "Models\\selector" }, Scale = new Property<Vector3> { Value = new Vector3(0.5f) } }; UIRenderer uiRenderer = new UIRenderer { Editable = false }; FPSInput input = new FPSInput { Editable = false }; input.EnabledWhenPaused.Value = true; AudioListener audioListener = new AudioListener { Editable = false }; Scroller scroller = new Scroller(); scroller.Position.Value = new Vector2(10, 10); scroller.AnchorPoint.Value = new Vector2(0, 0); scroller.ResizeHorizontal.Value = true; scroller.Name.Value = "Scroller"; uiRenderer.Root.Children.Add(scroller); ListContainer uiList = new ListContainer(); uiList.Name.Value = "PropertyList"; uiList.AnchorPoint.Value = new Vector2(0, 0); scroller.Children.Add(uiList); Container popup = new Container(); popup.Name.Value = "Popup"; popup.Opacity.Value = 0.5f; popup.Tint.Value = Microsoft.Xna.Framework.Color.Black; uiRenderer.Root.Children.Add(popup); ListContainer popupLayout = new ListContainer(); popup.Children.Add(popupLayout); Container popupSearchContainer = new Container(); popupSearchContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black; popupLayout.Children.Add(popupSearchContainer); TextElement popupSearch = new TextElement(); popupSearch.Name.Value = "PopupSearch"; popupSearch.FontFile.Value = "Font"; popupSearchContainer.Children.Add(popupSearch); Scroller popupScroller = new Scroller(); popupScroller.Size.Value = new Vector2(200.0f, 300.0f); popupLayout.Children.Add(popupScroller); ListContainer popupList = new ListContainer(); popupList.Name.Value = "PopupList"; popupScroller.Children.Add(popupList); result.Add("Editor", editor); result.Add("UI", ui); result.Add("UIRenderer", uiRenderer); result.Add("Model", model); result.Add("Input", input); result.Add("AudioListener", audioListener); result.Add("StartSpawnPoint", new Property<string>()); result.Add("ProceduralGenerator", new ProceduralGenerator()); return result; }
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); }
protected static void hideMessage(Container container, float delay = 0.0f) { ((GameMain)main).Menu.HideMessage(WorldFactory.Instance, container, delay); }
private void show(Entity entity) { foreach (DictionaryEntry entry in new DictionaryEntry[] { new DictionaryEntry("[" + entity.Type.ToString() + " entity]", entity.Properties.Concat(entity.Commands)) } .Union(entity.Components.Where(x => ((Component)x.Value).Editable))) { IEnumerable<DictionaryEntry> properties = null; if (typeof(Component).IsAssignableFrom(entry.Value.GetType())) properties = entry.Value.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) .Select(x => new DictionaryEntry(x.Name, x.GetValue(entry.Value))) .Concat(entry.Value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(y => y.GetIndexParameters().Length == 0) .Select(z => new DictionaryEntry(z.Name, z.GetValue(entry.Value, null)))); else properties = (IEnumerable<DictionaryEntry>)entry.Value; properties = properties.Where(x => x.Value != null && ((x.Value.GetType() == typeof(Command) && ((Command)x.Value).ShowInEditor) || (typeof(IProperty).IsAssignableFrom(x.Value.GetType()) && !typeof(IListProperty).IsAssignableFrom(x.Value.GetType()) && (bool)x.Value.GetType().GetProperty("Editable").GetValue(x.Value, null)))); if (properties.FirstOrDefault().Value == null) continue; Container label = this.addText(entry.Key.ToString()); Container propertyListContainer = new Container(); propertyListContainer.PaddingLeft.Value = 10.0f; propertyListContainer.PaddingRight.Value = 0.0f; propertyListContainer.PaddingBottom.Value = 0.0f; propertyListContainer.PaddingTop.Value = 0.0f; propertyListContainer.Opacity.Value = 0.0f; this.UIElements.Add(propertyListContainer); ListContainer propertyList = new ListContainer(); propertyListContainer.Children.Add(propertyList); label.Add(new Binding<float, bool>(label.Opacity, x => x ? 1.0f : 0.5f, label.Highlighted)); label.Add(new CommandBinding<Point>(label.MouseLeftUp, delegate(Point mouse) { propertyListContainer.Visible.Value = !propertyListContainer.Visible; })); foreach (DictionaryEntry property in properties) { ListContainer row = new ListContainer(); row.Orientation.Value = ListContainer.ListOrientation.Horizontal; Container keyContainer = new Container(); keyContainer.Tint.Value = Color.Black; keyContainer.Opacity.Value = 0.5f; keyContainer.ResizeHorizontal.Value = false; keyContainer.Size.Value = new Vector2(128.0f, 0.0f); TextElement keyText = new TextElement(); keyText.FontFile.Value = "Font"; keyText.Text.Value = property.Key.ToString(); keyContainer.Children.Add(keyText); row.Children.Add(keyContainer); if (property.Value.GetType() == typeof(Command)) { // It's a command Container field = new Container(); field.Tint.Value = Color.Black; field.Add(new Binding<float, bool>(field.Opacity, x => x ? 1.0f : 0.5f, field.Highlighted)); TextElement textField = new TextElement(); textField.FontFile.Value = "Font"; textField.Text.Value = "[Execute]"; field.Children.Add(textField); field.Add(new CommandBinding<Point>(field.MouseLeftUp, delegate(Point p) { ((Command)property.Value).Execute(); })); row.Children.Add(field); } else { // It's a property PropertyInfo info = property.Value.GetType().GetProperty("Value"); if (info.PropertyType.Equals(typeof(Vector2))) { ListContainer elementList = new ListContainer(); elementList.Orientation.Value = ListContainer.ListOrientation.Horizontal; row.Children.Add(elementList); foreach (VectorElement field in new[] { VectorElement.X, VectorElement.Y }) elementList.Children.Add(this.buildValueMemberField(info.PropertyType, (IProperty)property.Value, field)); } else if (info.PropertyType.Equals(typeof(Vector3))) { ListContainer elementList = new ListContainer(); elementList.Orientation.Value = ListContainer.ListOrientation.Horizontal; row.Children.Add(elementList); foreach (VectorElement field in new[] { VectorElement.X, VectorElement.Y, VectorElement.Z }) elementList.Children.Add(this.buildValueMemberField(info.PropertyType, (IProperty)property.Value, field)); } else if (info.PropertyType.Equals(typeof(Vector4)) || info.PropertyType.Equals(typeof(Quaternion)) || info.PropertyType.Equals(typeof(Color))) { ListContainer elementList = new ListContainer(); elementList.Orientation.Value = ListContainer.ListOrientation.Horizontal; row.Children.Add(elementList); foreach (VectorElement field in new[] { VectorElement.X, VectorElement.Y, VectorElement.Z, VectorElement.W }) elementList.Children.Add(this.buildValueMemberField(info.PropertyType, (IProperty)property.Value, field)); } else { UIComponent field = this.buildValueField((IProperty)property.Value, info); row.Children.Add(field); } } propertyList.Children.Add(row); } } }
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); }
private UIComponent buildValueField(IProperty property, PropertyInfo propertyInfo) { Container field = new Container(); field.Tint.Value = Color.Black; field.Add(new Binding<float, bool>(field.Opacity, x => x ? 1.0f : 0.5f, field.Highlighted)); TextElement textField = new TextElement(); textField.FontFile.Value = "Font"; field.Children.Add(textField); if (!propertyInfo.PropertyType.Equals(typeof(string))) { // Some kind of float, int, or bool field.Add(new CommandBinding<Point>(field.MouseLeftDown, delegate(Point mouse) { field.SwallowMouseEvents.Value = true; field.MouseLocked.Value = true; })); field.Add(new CommandBinding<Point>(field.MouseLeftUp, delegate(Point mouse) { field.SwallowMouseEvents.Value = false; field.MouseLocked.Value = false; })); } if (propertyInfo.PropertyType.Equals(typeof(int))) { Property<int> socket = (Property<int>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; socket.Value += scroll * (this.EnablePrecision ? 1 : 10); })); textField.Add(new Binding<string, int>(textField.Text, x => x.ToString(), socket)); } else if (propertyInfo.PropertyType.Equals(typeof(float))) { Property<float> socket = (Property<float>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; socket.Value += scroll * (this.EnablePrecision ? EditorUI.precisionDelta : EditorUI.normalDelta);; })); textField.Add(new Binding<string, float>(textField.Text, x => x.ToString("F"), socket)); } else if (propertyInfo.PropertyType.Equals(typeof(bool))) { Property<bool> socket = (Property<bool>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; socket.Value = !socket; })); textField.Add(new Binding<string, bool>(textField.Text, x => x.ToString(), socket)); } else if (typeof(Enum).IsAssignableFrom(propertyInfo.PropertyType)) { int numFields = propertyInfo.PropertyType.GetFields(BindingFlags.Static | BindingFlags.Public).Length; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; int i = (int)propertyInfo.GetValue(property, null); i += scroll; if (i < 0) i = numFields - 1; else if (i >= numFields) i = 0; propertyInfo.SetValue(property, Enum.ToObject(propertyInfo.PropertyType, i), null); })); textField.Add(new Binding<string>(textField.Text, () => propertyInfo.GetValue(property, null).ToString(), (IProperty)property)); } else if (propertyInfo.PropertyType.Equals(typeof(string))) { Property<string> socket = (Property<string>)property; Binding<string> binding = new Binding<string>(textField.Text, socket); textField.Add(binding); field.Add(new CommandBinding<Point>(field.MouseLeftUp, delegate(Point mouse) { if (this.selectedStringProperty != socket) { if (this.selectedStringProperty != null) this.revertStringProperty(); this.selectedStringProperty = socket; this.selectedStringDisplayProperty = textField.Text; binding.Enabled = false; this.selectedStringBinding = binding; this.selectedStringValue = socket.Value ?? ""; this.selectedStringIndex = this.selectedStringValue.Length; this.selectedStringDisplayProperty.Value = this.selectedStringValue.Insert(this.selectedStringIndex, "_"); this.StringPropertyLocked.Value = true; } })); } else if (propertyInfo.PropertyType.Equals(typeof(Entity.Handle))) { Property<Entity.Handle> socket = (Property<Entity.Handle>)property; Binding<string> binding = new Binding<string>(textField.Text, () => socket.Value.ID, socket); textField.Add(binding); field.Add(new CommandBinding<Point>(field.MouseLeftUp, delegate(Point mouse) { this.lockStringProperty(textField.Text, socket, socket.Value.ID ?? "", binding); })); } else if (propertyInfo.PropertyType.Equals(typeof(Matrix))) textField.Text.Value = "[matrix]"; return field; }
private UIComponent buildValueMemberField(Type type, IProperty property, VectorElement element) { Container field = new Container(); field.Tint.Value = Color.Black; field.Add(new Binding<float, bool>(field.Opacity, x => x ? 1.0f : 0.5f, field.Highlighted)); TextElement textField = new TextElement(); textField.FontFile.Value = "Font"; field.Children.Add(textField); field.Add(new CommandBinding<Point>(field.MouseLeftDown, delegate(Point mouse) { field.SwallowMouseEvents.Value = true; field.MouseLocked.Value = true; })); field.Add(new CommandBinding<Point>(field.MouseLeftUp, delegate(Point mouse) { field.SwallowMouseEvents.Value = false; field.MouseLocked.Value = false; })); if (type.Equals(typeof(Vector2))) { Property<Vector2> socket = (Property<Vector2>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; float delta = scroll * (this.EnablePrecision ? EditorUI.precisionDelta : EditorUI.normalDelta); socket.Value = socket.Value.SetElement(element, socket.Value.GetElement(element) + delta); })); textField.Add(new Binding<string, Vector2>(textField.Text, x => x.GetElement(element).ToString("F"), socket)); } else if (type.Equals(typeof(Vector3))) { Property<Vector3> socket = (Property<Vector3>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; float delta = scroll * (this.EnablePrecision ? EditorUI.precisionDelta : EditorUI.normalDelta); socket.Value = socket.Value.SetElement(element, socket.Value.GetElement(element) + delta); })); textField.Add(new Binding<string, Vector3>(textField.Text, x => x.GetElement(element).ToString("F"), socket)); } else if (type.Equals(typeof(Vector4))) { Property<Vector4> socket = (Property<Vector4>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; float delta = scroll * (this.EnablePrecision ? EditorUI.precisionDelta : EditorUI.normalDelta); socket.Value = socket.Value.SetElement(element, socket.Value.GetElement(element) + delta); })); textField.Add(new Binding<string, Vector4>(textField.Text, x => x.GetElement(element).ToString("F"), socket)); } else if (type.Equals(typeof(Quaternion))) { Property<Quaternion> socket = (Property<Quaternion>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; float delta = scroll * (this.EnablePrecision ? EditorUI.precisionDelta : EditorUI.normalDelta); socket.Value = socket.Value.SetElement(element, socket.Value.GetElement(element) + delta); })); textField.Add(new Binding<string, Quaternion>(textField.Text, x => x.GetElement(element).ToString("F"), socket)); } else if (type.Equals(typeof(Color))) { Property<Color> socket = (Property<Color>)property; field.Add(new CommandBinding<Point, int>(field.MouseScrolled, () => this.selectedStringProperty == null && field.MouseLocked, delegate(Point mouse, int scroll) { this.NeedsSave.Value = true; socket.Value = socket.Value.SetElement(element, (byte)Math.Max(0, Math.Min(255, socket.Value.GetElement(element) + scroll))); })); textField.Add(new Binding<string, Color>(textField.Text, x => x.GetElement(element).ToString(), socket)); } return field; }
private Container addText(string text) { Container container = new Container(); container.Tint.Value = Color.Black; container.Opacity.Value = 0.2f; TextElement display = new TextElement(); display.FontFile.Value = "Font"; display.Text.Value = text; container.Children.Add(display); this.UIElements.Add(container); return container; }
public override void InitializeProperties() { this.SelectedEntities.ItemAdded += new ListProperty<Entity>.ItemAddedEventHandler(delegate(int index, Entity item) { this.refresh(); }); this.SelectedEntities.ItemRemoved += new ListProperty<Entity>.ItemRemovedEventHandler(delegate(int index, Entity item) { this.refresh(); }); this.SelectedEntities.ItemChanged += new ListProperty<Components.Entity>.ItemChangedEventHandler(delegate(int index, Entity old, Entity newValue) { this.refresh(); }); this.SelectedEntities.Cleared += new ListProperty<Components.Entity>.ClearEventHandler(this.refresh); this.Add(new NotifyBinding(this.refresh, this.MapEditMode)); this.PopupVisible.Set = delegate(bool value) { if (this.PopupVisible.InternalValue && !value) this.revertStringProperty(); else if (!this.PopupVisible.InternalValue && value) this.lockStringProperty(this.PopupSearchText, null, this.PopupSearchText, null, false, true); this.PopupVisible.InternalValue = value; }; this.Add(new NotifyBinding(delegate() { string search = this.PopupSearchText.Value.ToLower().TrimEnd('_'); bool first = true; foreach (Container element in this.PopupElements) { PopupCommand command = (PopupCommand)element.UserData.Value; bool visible = command.Enabled() && command.Description.ToLower().Contains(search); element.Visible.Value = visible; if (first && visible) { first = false; element.Opacity.Value = 1.0f; } else element.Opacity.Value = 0.0f; } }, () => this.PopupVisible, this.PopupVisible, this.PopupSearchText)); this.Add(new ListBinding<UIComponent, PopupCommand>(this.PopupElements, this.PopupCommands, delegate(PopupCommand command) { Container container = new Container(); container.UserData.Value = command; container.Tint.Value = Color.Black; container.Add(new Binding<float, bool>(container.Opacity, x => x ? 1.0f : 0.0f, container.Highlighted)); container.Add(new CommandBinding<Point>(container.MouseLeftUp, delegate(Point p) { this.PopupVisible.Value = false; command.Action.Execute(); })); ListContainer layout = new ListContainer(); layout.Orientation.Value = ListContainer.ListOrientation.Horizontal; container.Children.Add(layout); Container descriptionContainer = new Container(); descriptionContainer.Opacity.Value = 0.0f; descriptionContainer.ResizeHorizontal.Value = false; descriptionContainer.Size.Value = new Vector2(110.0f, 0.0f); descriptionContainer.EnableScissor.Value = true; layout.Children.Add(descriptionContainer); TextElement description = new TextElement(); description.Name.Value = "Description"; description.FontFile.Value = "Font"; description.Text.Value = command.Description; descriptionContainer.Children.Add(description); if (command.Chord.Key != Keys.None) { Container chordContainer = new Container(); chordContainer.Opacity.Value = 0.0f; layout.Children.Add(chordContainer); TextElement chord = new TextElement(); chord.FontFile.Value = "Font"; if (command.Chord.Modifier != Keys.None) chord.Text.Value = command.Chord.Modifier.ToString() + " " + command.Chord.Key.ToString(); else chord.Text.Value = command.Chord.Key.ToString(); chordContainer.Children.Add(chord); } return new[] { container }; })); }
private static Container buildMessage() { Container msgBackground = new Container(); ((GameMain)main).UI.Root.GetChildByName("Messages").Children.Add(msgBackground); msgBackground.Tint.Value = Color.Black; msgBackground.Opacity.Value = 0.0f; TextElement msg = new TextElement(); msg.FontFile.Value = "Font"; msg.Opacity.Value = 0.0f; msg.WrapWidth.Value = 250.0f; msgBackground.Children.Add(msg); WorldFactory.Get().Add(msgBackground); return msgBackground; }
public void SaveWithNotification(bool overwrite) { if (!this.saving && !this.Paused && this.Menu.CanPause && this.MapFile != Main.MenuMap && !this.IsChallengeMap(this.MapFile) && PlayerFactory.Instance != null) { this.saving = true; Container saveNotification = new Container(); saveNotification.Tint.Value = Microsoft.Xna.Framework.Color.Black; saveNotification.Opacity.Value = UIFactory.Opacity; TextElement saveNotificationText = new TextElement(); saveNotificationText.Name.Value = "Text"; saveNotificationText.FontFile.Value = this.Font; saveNotificationText.Text.Value = "\\saving"; saveNotification.Children.Add(saveNotificationText); this.UI.Root.GetChildByName("Notifications").Children.Add(saveNotification); this.AddComponent(new Animation ( new Animation.Delay(0.01f), new Animation.Execute(delegate() { if (overwrite) this.SaveOverwrite(); else this.SaveNew(); }), new Animation.Delay(0.01f), new Animation.Set<string>(saveNotificationText.Text, "\\saved"), new Animation.Parallel ( new Animation.FloatMoveTo(saveNotification.Opacity, 0.0f, 1.0f), new Animation.FloatMoveTo(saveNotificationText.Opacity, 0.0f, 1.0f) ), new Animation.Execute(saveNotification.Delete), new Animation.Execute(delegate() { this.saving = false; }) )); } }
public static void Bind(Entity entity, Main main, ListContainer commandQueueContainer) { PCInput input = entity.Get<PCInput>(); Editor editor = entity.Get<Editor>(); EditorGeeUI gui = entity.Get<EditorGeeUI>(); 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 && Editor.EditorModelsVisible, analyticsEnable, Editor.EditorModelsVisible)); timelineScroller.Add(new Binding<bool>(timelineScroller.EnableScroll, x => !x, input.GetKey(Keys.LeftAlt))); entity.Add(new CommandBinding(entity.Delete, timelineScroller.Delete)); main.UI.Root.Children.Add(timelineScroller); timelineScroller.Add(new Binding<bool>(editor.EnableCameraDistanceScroll, () => !timelineScroller.Highlighted || editor.VoxelEditMode, timelineScroller.Highlighted, editor.VoxelEditMode)); timelineScroller.Add(new CommandBinding(timelineScroller.Delete, delegate() { editor.EnableCameraDistanceScroll.Value = true; })); ListContainer timelines = new ListContainer(); timelines.Alignment.Value = ListContainer.ListAlignment.Min; timelines.Orientation.Value = ListContainer.ListOrientation.Vertical; timelines.Reversed.Value = true; timelineScroller.Children.Add(timelines); input.Add(new CommandBinding<int>(input.MouseScrolled, () => input.GetKey(Keys.LeftAlt) && timelineScroller.Highlighted && !editor.VoxelEditMode, delegate(int delta) { 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); })); 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); EditorFactory.AddCommand ( entity, main, commandQueueContainer, "Load analytics data", new PCInput.Chord(), new Command { Action = delegate() { if (main.MapFile.Value != null) { List<Session> sessions = 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.AddAll(distinctEventNames.Keys.Select(x => new EventEntry { Name = x })); analyticsProperties.AddAll(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); } } } }, gui.MapCommands, () => !analyticsEnable && !string.IsNullOrEmpty(main.MapFile) && !gui.PickNextEntity, analyticsEnable, main.MapFile, gui.PickNextEntity ); 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 && Editor.EditorModelsVisible, analyticsEnable, Editor.EditorModelsVisible)); sessionsSidebar.Alignment.Value = ListContainer.ListAlignment.Max; sessionsSidebar.Reversed.Value = true; main.UI.Root.Children.Add(sessionsSidebar); entity.Add(new CommandBinding(entity.Delete, sessionsSidebar.Delete)); Func<string, ListContainer> createCheckboxListItem = delegate(string text) { ListContainer layout = new ListContainer(); layout.Orientation.Value = ListContainer.ListOrientation.Horizontal; TextElement label = new TextElement(); label.FontFile.Value = main.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 = UIFactory.Opacity; 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(string.Format("{0} {1:d} ({2})", entry.Session.UUID.Substring(0, 8), entry.Session.Date, new TimeSpan(0, 0, (int)entry.Session.TotalTime))); 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(item.MouseLeftDown, delegate() { if (entry.Active) { allSessions.Value = false; analyticsActiveSessions.Remove(entry); } else analyticsActiveSessions.Add(entry); })); return item; })); ListContainer allSessionsButton = createCheckboxListItem("[All]"); allSessionsButton.Add(new CommandBinding(allSessionsButton.MouseLeftDown, delegate() { 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 = UIFactory.Opacity; 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(colorHash(e.Name)); item.Add(new CommandBinding(item.MouseLeftDown, delegate() { if (e.Active) { allEvents.Value = false; analyticsActiveEvents.Remove(e); } else analyticsActiveEvents.Add(e); })); return item; })); ListContainer allEventsButton = createCheckboxListItem("[All]"); allEventsButton.Add(new CommandBinding(allEventsButton.MouseLeftDown, delegate() { 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 = UIFactory.Opacity; 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(colorHash(e.Name)); item.Add(new CommandBinding(item.MouseLeftDown, delegate() { if (e.Active) { allProperties.Value = false; analyticsActiveProperties.Remove(e); } else analyticsActiveProperties.Add(e); })); return item; })); ListContainer allPropertiesButton = createCheckboxListItem("[All]"); allPropertiesButton.Add(new CommandBinding(allPropertiesButton.MouseLeftDown, delegate() { 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 = 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 = colorHash(el.Name); int hash = (int)(new Color(color).PackedValue); foreach (Session.Event e in el.Events) { ModelInstance i = new ModelInstance(); i.Serialize = false; i.Setup("InstancedModels\\position-model", hash); if (i.IsFirstInstance) i.Model.Color.Value = new Vector3(color.X, color.Y, color.Z); i.Transform.Value = Matrix.CreateTranslation(positionProperty.GetLastRecordedPosition(e.Time)); models.Add(i); entity.Add(i); } eventPositionModels[el] = models; } } timeline.Children.AddAll(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.RemoveAll(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<Container> refreshPropertyGraph = delegate(Container container) { TextElement label = (TextElement)container.GetChildByName("Label"); LineDrawer2D lines = (LineDrawer2D)container.GetChildByName("Graph"); 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); } label.Text.Value = max.ToString("F"); } }; Action refreshPropertyGraphs = delegate() { foreach (Container propertyTimeline in propertyTimelines.Children) refreshPropertyGraph(propertyTimeline); }; 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 = UIFactory.Opacity; propertyTimeline.ResizeHorizontal.Value = false; propertyTimeline.ResizeVertical.Value = false; LineDrawer2D line = new LineDrawer2D(); line.Name.Value = "Graph"; line.Color.Value = colorHash(e.Name); line.UserData.Value = e; propertyTimeline.Children.Add(line); TextElement label = new TextElement(); label.FontFile.Value = main.Font; label.Name.Value = "Label"; label.Add(new Binding<Vector2>(label.Scale, x => new Vector2(1.0f / x.X, 1.0f / x.Y), timelines.Scale)); label.AnchorPoint.Value = new Vector2(0, 0); label.Position.Value = new Vector2(0, 0); propertyTimeline.Children.Add(label); refreshPropertyGraph(propertyTimeline); return 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 = colorHash(el.Name); int hash = (int)(new Color(color).PackedValue); foreach (Session.Event e in el.Events) { ModelInstance i = new ModelInstance(); i.Serialize = false; i.Setup("InstancedModels\\position-model", hash); if (i.IsFirstInstance) i.Model.Color.Value = new Vector3(color.X, color.Y, color.Z); i.Transform.Value = Matrix.CreateTranslation(positionProperty.GetLastRecordedPosition(e.Time)); entity.Add(i); models.Add(i); } eventPositionModels[el] = models; } } ModelInstance instance = new ModelInstance(); instance.Setup("InstancedModels\\position-model", 0); instance.Serialize = false; entity.Add(instance); sessionPositionModels.Add(s.Session, instance); s.Active.Value = true; timeline.Children.AddAll(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.RemoveAll(timeline.Children.Where(x => x.UserData.Value != null && ((Session.EventList)x.UserData.Value).Session == s.Session).ToList()); refreshPropertyGraphs(); }; entity.Add(new SetBinding<float>(playbackLocation, delegate(float value) { if (analyticsActiveSessions.Length == 0) return; if (value < 0.0f) playbackLocation.Value = 0.0f; float end = analyticsActiveSessions.Max(x => x.Session.TotalTime); if (value > end) { playbackLocation.Value = end; analyticsPlaying.Value = false; } 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); entity.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)); EditorFactory.AddCommand ( entity, main, commandQueueContainer, "Toggle analytics playback", new PCInput.Chord { Modifier = Keys.LeftAlt, Key = Keys.A }, new Command { Action = delegate() { analyticsPlaying.Value = !analyticsPlaying; } }, gui.MapCommands, () => analyticsEnable && !editor.MovementEnabled && analyticsActiveSessions.Length > 0, analyticsEnable, editor.MovementEnabled, analyticsActiveSessions.Length ); EditorFactory.AddCommand ( entity, main, commandQueueContainer, "Stop analytics playback", new PCInput.Chord { Key = Keys.Escape }, new Command { Action = delegate() { analyticsPlaying.Value = false; } }, gui.MapCommands, () => analyticsPlaying, analyticsPlaying ); Container playbackContainer = new Container(); playbackContainer.Tint.Value = Microsoft.Xna.Framework.Color.Black; playbackContainer.Opacity.Value = UIFactory.Opacity; sessionsSidebar.Children.Add(playbackContainer); playbackContainer.Add(new CommandBinding<int>(playbackContainer.MouseScrolled, delegate(int delta) { playbackSpeed.Value = Math.Max(1.0f, Math.Min(10.0f, playbackSpeed.Value + delta)); })); TextElement playbackLabel = new TextElement(); playbackLabel.FontFile.Value = main.Font; playbackLabel.Add(new Binding<string>(playbackLabel.Text, delegate() { return string.Format("{0} {1} {2:F}x", TimeTrialUI.SecondsToTimeString(playbackLocation), (analyticsPlaying ? "Playing" : "Stopped"), playbackSpeed); }, 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) { foreach (UIComponent component in timeline.Children) { LineDrawer2D lines = component as LineDrawer2D; if (lines == null) continue; Session.EventList el = lines.UserData.Value as Session.EventList; if (el != null) { bool stop = false; foreach (Session.Event e in el.Events) { if (el != null && (float)Math.Abs(e.Time - mouseRelative) < threshold) { descriptionContainer = new Container(); descriptionContainer.AnchorPoint.Value = new Vector2(0.5f, 1.0f); descriptionContainer.Position.Value = new Vector2(e.Time, 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; if (string.IsNullOrEmpty(e.Data)) description.Text.Value = el.Name; else description.Text.Value = string.Format("{0}\n{1}", el.Name, e.Data); description.FontFile.Value = main.Font; descriptionContainer.Children.Add(description); stop = true; break; } } if (stop) break; } } } } if (analyticsPlaying && !setTimelinePosition) { if (analyticsActiveSessions.Length == 0) analyticsPlaying.Value = false; else playbackLocation.Value += dt * playbackSpeed; } } ); entity.Add(timelineUpdate); timelineUpdate.EnabledInEditMode = true; }
public void ScheduleSave() { if (this.MapFile.Value != Main.MenuMap && (this.scheduledSave == null || !this.scheduledSave.Active)) { bool originalCanPause = this.Menu.CanPause; this.Menu.CanPause.Value = false; Container saveNotification = new Container(); TextElement saveNotificationText = new TextElement(); this.scheduledSave = new Animation ( new Animation.Delay(0.6f), new Animation.Execute(delegate() { Point size; #if VR if (this.VR) size = this.VRActualScreenSize; else #endif size = this.ScreenSize; this.Screenshot.Take(size); }), new Animation.Delay(0.01f), new Animation.Execute(delegate() { saveNotification.Tint.Value = Microsoft.Xna.Framework.Color.Black; saveNotification.Opacity.Value = UIFactory.Opacity; saveNotificationText.Name.Value = "Text"; saveNotificationText.FontFile.Value = this.Font; saveNotificationText.Text.Value = "\\saving"; saveNotification.Children.Add(saveNotificationText); this.UI.Root.GetChildByName("Notifications").Children.Add(saveNotification); }), new Animation.Delay(0.01f), new Animation.Execute(delegate() { this.SaveOverwrite(); }), new Animation.Delay(0.01f), new Animation.Set<string>(saveNotificationText.Text, "\\saved"), new Animation.Parallel ( new Animation.FloatMoveTo(saveNotification.Opacity, 0.0f, 1.0f), new Animation.FloatMoveTo(saveNotificationText.Opacity, 0.0f, 1.0f) ), new Animation.Execute(saveNotification.Delete) ); this.scheduledSave.Add(new CommandBinding(this.scheduledSave.Delete, delegate() { this.Screenshot.Clear(); this.UI.Root.GetChildByName("Notifications").Children.Clear(); this.Menu.CanPause.Value = originalCanPause; this.scheduledSave = null; })); WorldFactory.Instance.Add(this.scheduledSave); } }
public static void Run(Entity script) { const float fadeTime = 1.0f; main.Spawner.CanSpawn = false; Sprite logo = new Sprite(); logo.Image.Value = "Images\\logo"; logo.AnchorPoint.Value = new Vector2(0.5f, 0.5f); main.UI.Root.Children.Insert(0, logo); if (main.Spawner.StartSpawnPoint.Value == "demo") { main.UI.IsMouseVisible.Value = true; logo.Add(new Binding<Vector2, Point>(logo.Position, x => new Vector2(x.X * 0.5f, x.Y * 0.4f), main.ScreenSize)); Container listContainer = main.UIFactory.CreateContainer(); listContainer.Opacity.Value = 0.5f; listContainer.PaddingLeft.Value = listContainer.PaddingRight.Value = listContainer.PaddingBottom.Value = listContainer.PaddingTop.Value = 8.0f * main.FontMultiplier; listContainer.Add(new Binding<Vector2, Point>(listContainer.Position, x => new Vector2(x.X * 0.5f, x.Y * 0.65f), main.ScreenSize)); listContainer.AnchorPoint.Value = new Vector2(0.5f, 0.5f); listContainer.Opacity.Value = 0.0f; script.Add(new Animation ( new Animation.Delay(1.0f), new Animation.FloatMoveTo(listContainer.Opacity, 1.0f, fadeTime) )); ListContainer list = new ListContainer(); list.Spacing.Value = 8.0f * main.FontMultiplier; list.Alignment.Value = ListContainer.ListAlignment.Middle; listContainer.Children.Add(list); main.UI.Root.Children.Insert(1, listContainer); script.Add(new CommandBinding(script.Delete, listContainer.Delete)); Action<string> addText = delegate(string text) { TextElement element = new TextElement(); element.FontFile.Value = main.Font; element.Text.Value = text; element.Add(new Binding<float, Vector2>(element.WrapWidth, x => x.X, logo.ScaledSize)); element.Opacity.Value = 0.0f; script.Add(new Animation ( new Animation.Delay(1.0f), new Animation.FloatMoveTo(element.Opacity, 1.0f, fadeTime) )); list.Children.Add(element); }; Action<string, string> addLink = delegate(string text, string url) { TextElement element = main.UIFactory.CreateLink(text, url); element.Add(new Binding<float, Vector2>(element.WrapWidth, x => x.X, logo.ScaledSize)); element.Opacity.Value = 0.0f; script.Add(new Animation ( new Animation.Delay(1.0f), new Animation.FloatMoveTo(element.Opacity, 1.0f, fadeTime) )); list.Children.Add(element); }; addText("Thanks for trying the demo!"); addText("If you enjoyed it, please consider buying a Steam key from one of these venues:"); addLink("itch.io (best dev cut)", "http://et1337.itch.io/lemma"); addLink("Steam (direct)", "http://store.steampowered.com/app/300340"); } else logo.Add(new Binding<Vector2, Point>(logo.Position, x => new Vector2(x.X * 0.5f, x.Y * 0.5f), main.ScreenSize)); Container cornerContainer = main.UIFactory.CreateContainer(); cornerContainer.AnchorPoint.Value = new Vector2(1, 0); cornerContainer.PaddingLeft.Value = cornerContainer.PaddingRight.Value = 12.0f; #if VR if (main.VR) cornerContainer.Add(new Binding<Vector2, Point>(cornerContainer.Position, x => new Vector2(x.X * 0.75f, x.Y * 0.25f), main.ScreenSize)); else #endif cornerContainer.Add(new Binding<Vector2, Point>(cornerContainer.Position, x => new Vector2(x.X - 10.0f, 10.0f), main.ScreenSize)); main.UI.Root.Children.Add(cornerContainer); ListContainer corner = new ListContainer(); corner.Orientation.Value = ListContainer.ListOrientation.Horizontal; corner.Alignment.Value = ListContainer.ListAlignment.Middle; corner.Spacing.Value = 12.0f; cornerContainer.Children.Add(corner); TextElement version = new TextElement(); version.FontFile.Value = main.Font; version.Add(new Binding<string>(version.Text, x => string.Format(main.Strings.Get("build number") ?? "Build {0}", Main.Build.ToString()), main.Strings.Language)); corner.Children.Add(version); TextElement webLink = main.UIFactory.CreateLink("et1337.com", "http://et1337.com"); corner.Children.Add(webLink); Container languageMenu = new Container(); Container languageButton = main.UIFactory.CreateButton(delegate() { languageMenu.Visible.Value = !languageMenu.Visible; }); corner.Children.Add(languageButton); Sprite currentLanguageIcon = new Sprite(); currentLanguageIcon.Add(new Binding<string, Main.Config.Lang>(currentLanguageIcon.Image, x => "Images\\" + x.ToString(), main.Settings.Language)); languageButton.Children.Add(currentLanguageIcon); languageMenu.Tint.Value = Microsoft.Xna.Framework.Color.Black; languageMenu.Visible.Value = false; languageMenu.AnchorPoint.Value = new Vector2(1, 0); cornerContainer.CheckLayout(); languageMenu.Add(new Binding<Vector2>(languageMenu.Position, () => languageButton.GetAbsolutePosition() + new Vector2(languageButton.ScaledSize.Value.X, languageButton.ScaledSize.Value.Y), languageButton.Position, languageButton.ScaledSize, cornerContainer.Position)); main.UI.Root.Children.Add(languageMenu); ListContainer languages = new ListContainer(); languages.Orientation.Value = ListContainer.ListOrientation.Vertical; languages.Alignment.Value = ListContainer.ListAlignment.Max; languages.Spacing.Value = 0.0f; languageMenu.Children.Add(languages); foreach (Main.Config.Lang language in Enum.GetValues(typeof(Main.Config.Lang))) { UIComponent button = main.UIFactory.CreateButton(delegate() { main.Settings.Language.Value = language; languageMenu.Visible.Value = false; }); Sprite icon = new Sprite(); icon.Image.Value = "Images\\" + language.ToString(); button.Children.Add(icon); languages.Children.Add(button); } logo.Opacity.Value = 0.0f; version.Opacity.Value = 0.0f; cornerContainer.Opacity.Value = 0.0f; webLink.Opacity.Value = 0.0f; languageButton.Opacity.Value = 0.0f; currentLanguageIcon.Opacity.Value = 0.0f; script.Add(new Animation ( new Animation.Delay(1.0f), new Animation.Parallel ( new Animation.FloatMoveTo(logo.Opacity, 1.0f, fadeTime), new Animation.FloatMoveTo(version.Opacity, 1.0f, fadeTime), new Animation.FloatMoveTo(cornerContainer.Opacity, UIFactory.Opacity, fadeTime), new Animation.FloatMoveTo(webLink.Opacity, 1.0f, fadeTime), new Animation.FloatMoveTo(languageButton.Opacity, UIFactory.Opacity, fadeTime), new Animation.FloatMoveTo(currentLanguageIcon.Opacity, 1.0f, fadeTime) ) )); script.Add(new CommandBinding(script.Delete, logo.Delete, cornerContainer.Delete, languageMenu.Delete)); main.Renderer.InternalGamma.Value = 0.0f; main.Renderer.Brightness.Value = 0.0f; main.Renderer.Tint.Value = new Vector3(0.0f); script.Add(new Animation ( new Animation.Vector3MoveTo(main.Renderer.Tint, new Vector3(1.0f), 0.3f) )); if (main.Settings.GodModeProperty) SteamWorker.SetAchievement("cheevo_god_mode"); else { int konamiIndex = 0; PCInput input = script.Create<PCInput>(); input.Add(new CommandBinding<PCInput.PCInputBinding>(input.AnyInputDown, delegate(PCInput.PCInputBinding button) { if (!main.Settings.GodModeProperty) { if (button.Key == konamiCode[konamiIndex].Key || button.GamePadButton == konamiCode[konamiIndex].GamePadButton) { if (konamiIndex == konamiCode.Length - 1) { main.Settings.GodModeProperty.Value = true; main.SaveSettings(); SteamWorker.SetAchievement("cheevo_god_mode"); main.Menu.HideMessage(script, main.Menu.ShowMessage(script, "\\god mode"), 5.0f); } else konamiIndex++; } else { konamiIndex = 0; if (button.Key == konamiCode[konamiIndex].Key || button.GamePadButton == konamiCode[konamiIndex].GamePadButton) konamiIndex++; } } })); } }