public override void Bind(Entity result, Main main, bool creating = false) { this.SetMain(result, main); Transform transform = result.Get<Transform>(); Sound sound = result.Get<Sound>("Sound"); sound.Add(new Binding<Vector3>(sound.Position, transform.Position)); result.CannotSuspendByDistance = !sound.Is3D; result.Add(new NotifyBinding(delegate() { result.CannotSuspendByDistance = !sound.Is3D; }, sound.Is3D)); Property<float> min = result.GetProperty<float>("MinimumInterval"); Property<float> max = result.GetProperty<float>("MaximumInterval"); Random random = new Random(); float interval = min + ((float)random.NextDouble() * (max - min)); result.Add(new Updater { delegate(float dt) { if (!sound.IsPlaying) interval -= dt; if (interval <= 0) { sound.Play.Execute(); interval = min + ((float)random.NextDouble() * (max - min)); } } }); }
public override void Bind(Entity result, Main main, bool creating = false) { this.SetMain(result, main); Transform transform = result.Get<Transform>(); AnimatedModel model = result.Get<AnimatedModel>("Model"); model.Add(new Binding<Matrix>(model.Transform, transform.Matrix)); Property<string> animation = result.GetProperty<string>("Animation"); Property<bool> loop = result.GetProperty<bool>("Loop"); model.Add(new NotifyBinding(delegate() { model.Stop(); if (animation != null) model.StartClip(animation, 0, loop); }, animation, loop)); }
public override void Bind(Entity result, Main main, bool creating = false) { result.CannotSuspend = true; Script script = result.Get<Script>(); Property<bool> executeOnLoad = result.GetProperty<bool>("ExecuteOnLoad"); if (executeOnLoad && !main.EditorEnabled) { result.Add("Executor", new PostInitialization { delegate() { if (executeOnLoad) script.Execute.Execute(); } }); } Property<bool> deleteOnExecute = result.GetOrMakeProperty<bool>("DeleteOnExecute", true, false); if (deleteOnExecute) result.Add(new CommandBinding(script.Execute, result.Delete)); this.SetMain(result, main); }
protected override void Validate(Entity entity, RuleArgs e) { var value = Convert.ToDouble(entity.GetProperty(e.Property)); var min = this.Min; if (value < min) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format("{0} 不能低于 {1}。".Translate(), e.DisplayProperty(), min); } } else { var max = this.Max; if (value > max) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format("{0} 不能超过 {1}。".Translate(), e.DisplayProperty(), max); } } } }
protected override void Validate(Entity entity, RuleArgs e) { var value = Convert.ToDouble(entity.GetProperty(e.Property)); if (value <= 0) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format("{0} 需要是正数。".Translate(), e.DisplayProperty()); } } }
public override void AttachEditorComponents(Entity result, Main main) { Model model = new Model(); model.Filename.Value = "Models\\light"; model.Color.Value = this.Color; model.Editable = false; model.Serialize = false; result.Add("EditorModel", model); Transform transform = result.Get<Transform>(); Property<Direction> dir = result.GetProperty<Direction>("Direction"); model.Add(new Binding<Matrix>(model.Transform, delegate() { Vector3 pos = transform.Position; return PlatformFactory.rotationMatrices[(int)dir.Value] * Matrix.CreateTranslation(pos); }, transform.Position, dir)); }
protected override void Validate(Entity entity, RuleArgs e) { var value = entity.GetProperty(e.Property) as string; if (!string.IsNullOrEmpty(value)) { var min = this.Min; if (value.Length < min) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format( "{0} 不能低于 {1} 个字符。".Translate(), e.DisplayProperty(), min ); } } else { var max = this.Max; if (value.Length > max) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format( "{0} 不能超过 {1} 个字符。".Translate(), e.DisplayProperty(), max ); } } } } }
public override void Bind(Entity result, Main main, bool creating = false) { result.CannotSuspend = true; Transform transform = result.Get<Transform>(); PhysicsBlock physics = result.Get<PhysicsBlock>(); ModelInstance model = result.Get<ModelInstance>(); physics.Add(new TwoWayBinding<Matrix>(transform.Matrix, physics.Transform)); Property<string> soundCue = result.GetProperty<string>("CollisionSoundCue"); Property<Vector3> scale = new Property<Vector3> { Value = Vector3.One }; model.Add(new Binding<Matrix>(model.Transform, () => Matrix.CreateScale(scale) * transform.Matrix, scale, transform.Matrix)); const float volumeMultiplier = 0.003f; physics.Add(new CommandBinding<Collidable, ContactCollection>(physics.Collided, delegate(Collidable collidable, ContactCollection contacts) { float volume = contacts[contacts.Count - 1].NormalImpulse * volumeMultiplier; if (volume > 0.2f && soundCue.Value != null) { Sound sound = Sound.PlayCue(main, soundCue, transform.Position, volume, 0.05f); if (sound != null) sound.GetProperty("Pitch").Value = 1.0f; } })); result.Add("Fade", new Animation ( new Animation.Delay(5.0f), new Animation.Vector3MoveTo(scale, Vector3.Zero, 1.0f), new Animation.Execute(delegate() { result.Delete.Execute(); }) )); this.SetMain(result, main); PhysicsBlock.CancelPlayerCollisions(physics); }
protected override void Validate(Entity entity, RuleArgs e) { var value = (string)entity.GetProperty(e.Property) ?? string.Empty; if (!string.IsNullOrEmpty(value)) { if (!this.Regex.IsMatch(value)) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format( "{0} 必须是 {1}。".Translate(), e.DisplayProperty(), this.RegexLabel.Translate() ); } } } }
protected override void Validate(Entity entity, RuleArgs e) { var property = e.Property; bool isNull = false; if (property is IRefProperty) { var id = entity.GetRefNullableId((property as IRefProperty).RefIdProperty); isNull = id == null; } else { var value = entity.GetProperty(property); if (property.PropertyType == typeof(string)) { isNull = string.IsNullOrEmpty(value as string); } else { isNull = value == null; } } if (isNull) { if (this.MessageBuilder != null) { e.BrokenDescription = this.MessageBuilder(entity); } else { e.BrokenDescription = string.Format("{0} 里没有输入值。".Translate(), e.DisplayProperty()); } } }
public override void Bind(Entity result, Main main, bool creating = false) { base.Bind(result, main); Transform transform = result.Get<Transform>(); DynamicMap map = result.Get<DynamicMap>(); Direction initialDirection = result.GetProperty<Direction>("Direction"); Direction dir = initialDirection; Entity.Handle limit1 = result.GetProperty<Entity.Handle>("Limit 1"); Entity.Handle limit2 = result.GetProperty<Entity.Handle>("Limit 2"); Property<bool> isAtStart = new Property<bool> { Editable = false, Serialize = false }; Property<bool> isAtEnd = new Property<bool> { Editable = false, Serialize = false }; result.Add("IsAtStart", isAtStart); result.Add("IsAtEnd", isAtEnd); EntityMover mover = null; EntityRotator rotator = null; if (!main.EditorEnabled) { mover = new EntityMover(map.PhysicsEntity); mover.TargetPosition = transform.Position; main.Space.Add(mover); rotator = new EntityRotator(map.PhysicsEntity); rotator.TargetOrientation = transform.Quaternion; main.Space.Add(rotator); } Vector3 targetPosition = transform.Position; Property<float> speed = result.GetProperty<float>("Speed"); Property<bool> stopOnEnd = result.GetProperty<bool>("StopOnEnd"); Updater update = null; update = new Updater { delegate(float dt) { if (!result.Active || limit1.Target == null || limit2.Target == null) return; float currentLocation = targetPosition.GetComponent(dir); targetPosition = targetPosition.SetComponent(dir, currentLocation + dt * speed); mover.TargetPosition = targetPosition; float limit1Location = limit1.Target.Get<Transform>().Position.Value.GetComponent(dir); float limit2Location = limit2.Target.Get<Transform>().Position.Value.GetComponent(dir); float limitLocation = Math.Max(limit1Location, limit2Location); if (currentLocation > limitLocation) { dir = dir.GetReverse(); if (limitLocation == limit1Location) { isAtStart.Value = true; isAtEnd.Value = false; } else { isAtStart.Value = false; isAtEnd.Value = true; } if (stopOnEnd) update.Enabled.Value = false; } } }; update.Add(new TwoWayBinding<bool>(result.GetProperty<bool>("Enabled"), update.Enabled)); result.Add(update); }
private static void attachEditorComponents(Entity result, Main main) { Transform transform = result.Get<Transform>(); Property<Entity.Handle> parentMap = result.GetOrMakeProperty<Entity.Handle>("Parent"); EntityConnectable.AttachEditorComponents(result, parentMap); Model model = new Model(); model.Filename.Value = "Models\\cone"; model.Editable = false; model.Serialize = false; result.Add("DirectionModel", model); Property<Direction> dir = result.GetProperty<Direction>("Direction"); Transform mapTransform = result.Get<Transform>("MapTransform"); model.Add(new Binding<Matrix>(model.Transform, delegate() { Matrix m = Matrix.Identity; m.Translation = transform.Position; if (dir == Direction.None) m.Forward = m.Right = m.Up = Vector3.Zero; else { Vector3 normal = Vector3.TransformNormal(dir.Value.GetVector(), mapTransform.Matrix); m.Forward = -normal; if (normal.Equals(Vector3.Up)) m.Right = Vector3.Left; else if (normal.Equals(Vector3.Down)) m.Right = Vector3.Right; else m.Right = Vector3.Normalize(Vector3.Cross(normal, Vector3.Down)); m.Up = Vector3.Cross(normal, m.Left); } return m; }, transform.Matrix, mapTransform.Matrix)); }
public override void Bind(Entity result, Main main, bool creating = false) { Transform transform = result.Get<Transform>(); EnemyBase enemy = result.GetOrCreate<EnemyBase>("Base"); PlayerCylinderTrigger trigger = result.Get<PlayerCylinderTrigger>(); PointLight light = result.GetOrCreate<PointLight>(); light.Color.Value = new Vector3(1.3f, 0.5f, 0.5f); light.Attenuation.Value = 15.0f; light.Shadowed.Value = false; light.Serialize = false; ListProperty<Entity.Handle> dynamicMaps = result.GetListProperty<Entity.Handle>("DynamicMaps"); Property<float> timeUntilRebuild = result.GetProperty<float>("TimeUntilRebuild"); Property<float> timeUntilRebuildComplete = result.GetProperty<float>("TimeUntilRebuildComplete"); Property<float> rebuildDelay = result.GetProperty<float>("RebuildDelay"); Property<float> rebuildTime = result.GetProperty<float>("RebuildTime"); const float rebuildTimeMultiplier = 0.03f; enemy.Add(new CommandBinding(enemy.Delete, result.Delete)); enemy.Add(new Binding<Matrix>(enemy.Transform, transform.Matrix)); light.Add(new Binding<Vector3>(light.Position, enemy.Position)); trigger.Add(new Binding<Matrix>(trigger.Transform, () => Matrix.CreateTranslation(0.0f, 0.0f, enemy.Offset) * transform.Matrix, transform.Matrix, enemy.Offset)); Action<Entity> fall = delegate(Entity player) { if (timeUntilRebuild.Value > 0 || timeUntilRebuildComplete.Value > 0) return; if (!enemy.IsValid) { result.Delete.Execute(); return; } // Disable the cell-emptied notification. // This way, we won't think that the base has been destroyed by the player. // We are not in fact dying, we're just destroying the base so we can fall over. enemy.EnableCellEmptyBinding = false; Map m = enemy.Map.Value.Target.Get<Map>(); m.Empty(enemy.BaseBoxes.SelectMany(x => x.GetCoords())); m.Regenerate(delegate(List<DynamicMap> spawnedMaps) { Vector3 playerPos = player.Get<Transform>().Position; playerPos += player.Get<Player>().LinearVelocity.Value * 0.65f; foreach (DynamicMap newMap in spawnedMaps) { Vector3 toPlayer = playerPos - newMap.PhysicsEntity.Position; toPlayer.Normalize(); if (Math.Abs(toPlayer.Y) < 0.9f) { toPlayer *= 25.0f * newMap.PhysicsEntity.Mass; Vector3 positionAtPlayerHeight = newMap.PhysicsEntity.Position; Vector3 impulseAtBase = toPlayer * -0.75f; impulseAtBase.Y = 0.0f; positionAtPlayerHeight.Y = playerPos.Y; newMap.PhysicsEntity.ApplyImpulse(ref positionAtPlayerHeight, ref impulseAtBase); newMap.PhysicsEntity.ApplyLinearImpulse(ref toPlayer); } newMap.PhysicsEntity.Material.KineticFriction = 1.0f; newMap.PhysicsEntity.Material.StaticFriction = 1.0f; dynamicMaps.Add(newMap.Entity); } }); timeUntilRebuild.Value = rebuildDelay; }; result.Add(new PostInitialization { delegate() { foreach (Entity.Handle map in dynamicMaps) { if (map.Target != null) { BEPUphysics.Entities.MorphableEntity e = map.Target.Get<DynamicMap>().PhysicsEntity; e.Material.KineticFriction = 1.0f; e.Material.StaticFriction = 1.0f; } } } }); result.Add(new CommandBinding<Entity>(trigger.PlayerEntered, fall)); result.Add(new Updater { delegate(float dt) { if (timeUntilRebuild > 0) { if (enemy.Map.Value.Target == null || !enemy.Map.Value.Target.Active) { result.Delete.Execute(); return; } float newValue = Math.Max(0.0f, timeUntilRebuild.Value - dt); timeUntilRebuild.Value = newValue; if (newValue == 0.0f) { // Rebuild Map m = enemy.Map.Value.Target.Get<Map>(); int index = 0; Vector3 baseCenter = Vector3.Zero; EffectBlockFactory factory = Factory.Get<EffectBlockFactory>(); Entity targetMap = enemy.Map.Value.Target; foreach (Map.Coordinate c in enemy.BaseBoxes.SelectMany(x => x.GetCoords())) { if (m[c].ID == 0) { Entity block = factory.CreateAndBind(main); c.Data.ApplyToEffectBlock(block.Get<ModelInstance>()); block.GetProperty<Vector3>("Offset").Value = m.GetRelativePosition(c); block.GetProperty<Vector3>("StartPosition").Value = m.GetAbsolutePosition(c) + new Vector3(0.25f, 0.5f, 0.25f) * index; block.GetProperty<Matrix>("StartOrientation").Value = Matrix.CreateRotationX(0.15f * index) * Matrix.CreateRotationY(0.15f * index); block.GetProperty<float>("TotalLifetime").Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime); factory.Setup(block, targetMap, c, c.Data.ID); main.Add(block); index++; baseCenter += new Vector3(c.X, c.Y, c.Z); } } baseCenter /= index; // Get the average position of the base cells foreach (Entity.Handle e in dynamicMaps) { Entity dynamicMap = e.Target; Map dynamicMapComponent = dynamicMap != null && dynamicMap.Active ? dynamicMap.Get<Map>() : null; if (dynamicMap == null || !dynamicMap.Active) continue; Matrix orientation = dynamicMapComponent.Transform.Value; orientation.Translation = Vector3.Zero; List<Map.Coordinate> coords = new List<Map.Coordinate>(); foreach (Map.Coordinate c in dynamicMapComponent.Chunks.SelectMany(x => x.Boxes).SelectMany(x => x.GetCoords())) { if (m[c].ID == 0) coords.Add(c); } foreach (Map.Coordinate c in coords.OrderBy(x => (new Vector3(x.X, x.Y, x.Z) - baseCenter).LengthSquared())) { Entity block = factory.CreateAndBind(main); c.Data.ApplyToEffectBlock(block.Get<ModelInstance>()); block.GetProperty<Vector3>("Offset").Value = m.GetRelativePosition(c); block.GetProperty<bool>("Scale").Value = dynamicMapComponent == null; if (dynamicMapComponent != null && dynamicMapComponent[c].ID == c.Data.ID) { block.GetProperty<Vector3>("StartPosition").Value = dynamicMapComponent.GetAbsolutePosition(c); block.GetProperty<Matrix>("StartOrientation").Value = orientation; } else { block.GetProperty<Vector3>("StartPosition").Value = m.GetAbsolutePosition(c) + new Vector3(0.25f, 0.5f, 0.25f) * index; block.GetProperty<Matrix>("StartOrientation").Value = Matrix.CreateRotationX(0.15f * index) * Matrix.CreateRotationY(0.15f * index); } block.GetProperty<float>("TotalLifetime").Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime); factory.Setup(block, targetMap, c, c.Data.ID); main.Add(block); index++; } dynamicMap.Delete.Execute(); } timeUntilRebuildComplete.Value = 0.05f + (index * rebuildTimeMultiplier * rebuildTime); dynamicMaps.Clear(); } } else if (timeUntilRebuildComplete > 0) { // Rebuilding float newValue = Math.Max(0.0f, timeUntilRebuildComplete.Value - dt); timeUntilRebuildComplete.Value = newValue; if (newValue == 0.0f) { // Done rebuilding if (!enemy.IsValid) result.Delete.Execute(); else { enemy.EnableCellEmptyBinding = !main.EditorEnabled; if (trigger.IsTriggered) fall(trigger.Player.Value.Target); } } } } }); this.SetMain(result, main); }
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) )); })); }
private static bool isPersistent(Entity entity) { if (MapExitFactory.persistentTypes.Contains(entity.Type)) return true; if (MapExitFactory.attachedTypes.Contains(entity.Type)) { Property<bool> attached = entity.GetProperty<bool>("Attached"); if (attached != null && attached) return true; } return false; }
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 )); }
private static void attachEditorComponents(Entity result, Main main) { Transform transform = result.Get<Transform>(); Property<bool> selected = new Property<bool> { Value = false, Editable = false, Serialize = false }; result.Add("EditorSelected", selected); Property<Entity.Handle> parentMap = result.GetOrMakeProperty<Entity.Handle>("Parent"); Command<Entity> toggleEntityConnected = new Command<Entity> { Action = delegate(Entity entity) { parentMap.Value = entity; } }; result.Add("ToggleEntityConnected", toggleEntityConnected); LineDrawer connectionLines = new LineDrawer { Serialize = false }; connectionLines.Add(new Binding<bool>(connectionLines.Enabled, selected)); Color connectionLineColor = new Color(1.0f, 1.0f, 1.0f, 0.5f); Action recalculateLine = delegate() { connectionLines.Lines.Clear(); Entity parent = parentMap.Value.Target; if (parent != null) { connectionLines.Lines.Add(new LineDrawer.Line { A = new Microsoft.Xna.Framework.Graphics.VertexPositionColor(transform.Position, connectionLineColor), B = new Microsoft.Xna.Framework.Graphics.VertexPositionColor(parent.Get<Transform>().Position, connectionLineColor) }); } }; Model model = new Model(); model.Filename.Value = "Models\\cone"; model.Editable = false; model.Serialize = false; result.Add("DirectionModel", model); Property<Direction> dir = result.GetProperty<Direction>("Direction"); Transform mapTransform = result.Get<Transform>("MapTransform"); model.Add(new Binding<Matrix>(model.Transform, delegate() { Matrix m = Matrix.Identity; m.Translation = transform.Position; if (dir == Direction.None) m.Forward = m.Right = m.Up = Vector3.Zero; else { Vector3 normal = Vector3.TransformNormal(dir.Value.GetVector(), mapTransform.Matrix); m.Forward = -normal; if (normal.Equals(Vector3.Up)) m.Right = Vector3.Left; else if (normal.Equals(Vector3.Down)) m.Right = Vector3.Right; else m.Right = Vector3.Normalize(Vector3.Cross(normal, Vector3.Down)); m.Up = Vector3.Cross(normal, m.Left); } return m; }, transform.Matrix, mapTransform.Matrix)); NotifyBinding recalculateBinding = null; Action rebuildBinding = delegate() { if (recalculateBinding != null) { connectionLines.Remove(recalculateBinding); recalculateBinding = null; } if (parentMap.Value.Target != null) { recalculateBinding = new NotifyBinding(recalculateLine, parentMap.Value.Target.Get<Transform>().Matrix); connectionLines.Add(recalculateBinding); } recalculateLine(); }; connectionLines.Add(new NotifyBinding(rebuildBinding, parentMap)); connectionLines.Add(new NotifyBinding(recalculateLine, selected)); connectionLines.Add(new NotifyBinding(recalculateLine, () => selected, transform.Position)); result.Add(connectionLines); }
/// <summary> /// 递归读取根对象的所有子对象 /// </summary> /// <param name="entity"></param> private void ReadChildren(Entity entity) { var allProperties = entity.PropertiesContainer.GetNonReadOnlyCompiledProperties(); var childrenList = new List<IList<Entity>>(); //遍历所有子属性,读取孩子列表 for (int i = 0, c = allProperties.Count; i < c; i++) { var property = allProperties[i]; if (property is IListProperty) { var children = entity.GetProperty(property) as IList<Entity>; if (children != null && children.Count > 0) { //所有孩子列表中的实体,都加入到对应的实体列表中。 //并递归读取孩子的孩子实体。 var entityType = children[0].GetType(); var list = this.FindAggregateList(entityType); childrenList.Add(list); for (int j = 0, c2 = children.Count; j < c2; j++) { var child = children[j]; list.Add(child); this.ReadChildren(child); } } } } }
private string BuildError(Entity entity) { if (this.MessageBuilder != null) { return this.MessageBuilder(entity); } var propertyFormat = "属性 {0} 的值是 {1}".Translate(); var error = new StringBuilder("已经存在"); bool first = true; foreach (IProperty property in this.Properties) { if (!first) error.Append("、".Translate()); first = false; var value = entity.GetProperty(property); error.AppendFormat(propertyFormat, Display(property), value); } error.Append(" 的实体 ".Translate()) .Append(Display(entity.GetType())); return error.ToString(); }
/// <summary> /// 根据传入的属性列表,来构造 CommonQueryCriteria /// 返回是否有非空属性需要验证。 /// </summary> /// <param name="entity"></param> /// <param name="criteria"></param> /// <returns></returns> private bool AddToCriteria(Entity entity, CommonQueryCriteria criteria) { bool hasValue = false; foreach (IProperty property in this.Properties) { EnsurePropertyCategory(property); var value = entity.GetProperty(property); if (DomainHelper.IsNotEmpty(value)) { hasValue = true; criteria.Add(property, value); } } if (entity.HasId) { criteria.Add(Entity.IdProperty, PropertyOperator.NotEqual, entity.Id); } return hasValue; }
public override void Bind(Entity result, Main main, bool creating = false) { Factory.Get<DynamicMapFactory>().Bind(result, main); Transform transform = result.Get<Transform>(); DynamicMap map = result.Get<DynamicMap>(); PointLight light = result.Get<PointLight>(); Sound blastFireSound = result.Get<Sound>("BlastFireSound"); blastFireSound.Add(new Binding<Vector3>(blastFireSound.Position, transform.Position)); blastFireSound.Add(new Binding<Vector3>(blastFireSound.Velocity, map.LinearVelocity)); Sound blastChargeSound = result.Get<Sound>("BlastChargeSound"); blastChargeSound.Add(new Binding<Vector3>(blastChargeSound.Position, transform.Position)); blastChargeSound.Add(new Binding<Vector3>(blastChargeSound.Velocity, map.LinearVelocity)); map.Add(new CommandBinding(map.CompletelyEmptied, delegate() { if (!main.EditorEnabled) result.Delete.Execute(); })); EntityRotator rotator = null; EntityMover mover = null; if (!main.EditorEnabled) { rotator = new EntityRotator(map.PhysicsEntity); main.Space.Add(rotator); mover = new EntityMover(map.PhysicsEntity); mover.TargetPosition = transform.Position; main.Space.Add(mover); } Map.Coordinate blastSource = map.GetCoordinate(0, 0, 0); Map.Coordinate blastPosition = blastSource; Map.CellState criticalMaterial = WorldFactory.StatesByName["Critical"]; foreach (Map.Box box in map.Chunks.SelectMany(x => x.Boxes)) { if (box.Type == criticalMaterial) { blastSource = map.GetCoordinate(box.X, box.Y, box.Z); blastPosition = map.GetCoordinate(box.X, box.Y, box.Z - 3); break; } } Property<float> blastIntervalTime = result.GetProperty<float>("BlastInterval"); float blastInterval = 0.0f; Property<float> playerPositionMemoryTime = result.GetProperty<float>("PlayerPositionMemoryTime"); float timeSinceLastSpottedPlayer = playerPositionMemoryTime; Property<float> visibilityCheckInterval = result.GetProperty<float>("VisibilityCheckInterval"); float timeSinceLastVisibilityCheck = 0.0f; Property<float> blastChargeTime = result.GetProperty<float>("BlastChargeTime"); float blastCharge = 0.0f; Property<float> blastSpeed = result.GetProperty<float>("BlastSpeed"); Property<float> playerDetectionRadius = result.GetProperty<float>("PlayerDetectionRadius"); Updater update = new Updater(); update.Add(delegate(float dt) { if (map[blastSource].ID == 0) { update.Delete.Execute(); if (rotator != null) { main.Space.Remove(rotator); main.Space.Remove(mover); } light.Delete.Execute(); return; } Entity player = PlayerFactory.Instance; if (player != null) { Vector3 playerPosition = player.Get<Transform>().Position.Value; Vector3 rayStart = map.GetAbsolutePosition(blastPosition); Vector3 rayDirection = playerPosition - rayStart; rayDirection.Normalize(); timeSinceLastVisibilityCheck += dt; if (timeSinceLastVisibilityCheck > visibilityCheckInterval) { if ((playerPosition - transform.Position).Length() < playerDetectionRadius) timeSinceLastSpottedPlayer = 0.0f; else if (Vector3.Dot(rayDirection, map.GetAbsoluteVector(Vector3.Forward)) > 0) { RayCastResult hit; if (main.Space.RayCast(new Ray(rayStart, rayDirection), out hit)) { EntityCollidable collidable = hit.HitObject as EntityCollidable; if (collidable != null && collidable.Entity.Tag is Player) timeSinceLastSpottedPlayer = 0.0f; } } timeSinceLastVisibilityCheck = 0.0f; } timeSinceLastSpottedPlayer += dt; light.Attenuation.Value = 0.0f; if (timeSinceLastSpottedPlayer < playerPositionMemoryTime) { rotator.TargetOrientation = Quaternion.CreateFromRotationMatrix(Matrix.Invert(Matrix.CreateLookAt(rayStart, playerPosition, Vector3.Up))); if (blastInterval > blastIntervalTime) { if (blastCharge < blastChargeTime) { if (blastCharge == 0.0f) blastChargeSound.Play.Execute(); blastCharge += dt; light.Position.Value = rayStart; light.Attenuation.Value = (blastCharge / blastChargeTime) * 30.0f; } else { blastCharge = 0.0f; blastFireSound.Play.Execute(); blastInterval = 0.0f; Entity blast = Factory.CreateAndBind(main, "Blast"); PhysicsBlock physics = blast.Get<PhysicsBlock>(); Transform blastTransform = blast.Get<Transform>(); blastTransform.Position.Value = rayStart; physics.LinearVelocity.Value = (rayDirection * blastSpeed) + new Vector3(0.0f, 6.0f, 0.0f); main.Add(blast); } } else { blastInterval += dt; blastCharge = 0.0f; } } else blastCharge = 0.0f; } }); result.Add("Update", update); }
public void Setup(Entity result, Entity m, Map.Coordinate c, int s) { Property<Entity.Handle> map = result.GetProperty<Entity.Handle>("TargetMap"); Property<Map.Coordinate> coord = result.GetProperty<Map.Coordinate>("TargetCoord"); Property<int> stateId = result.GetProperty<int>("TargetCellStateID"); map.InternalValue = m; coord.InternalValue = c; stateId.InternalValue = s; stateId.Changed(); }
public override void Bind(Entity result, Main main, bool creating = false) { result.CannotSuspend = true; result.Add(new TwoWayBinding<string>(result.GetProperty<string>("LightRampTexture"), main.Renderer.LightRampTexture)); result.Add(new TwoWayBinding<string>(result.GetOrMakeProperty<string>("EnvironmentMap", true, "Maps\\env0"), main.Renderer.EnvironmentMap)); result.Add(new TwoWayBinding<Vector3>(result.GetOrMakeProperty<Vector3>("EnvironmentColor", true, Vector3.One), main.Renderer.EnvironmentColor)); result.Add(new TwoWayBinding<Color>(result.GetProperty<Color>("BackgroundColor"), main.Renderer.BackgroundColor)); result.Add(new TwoWayBinding<float>(result.GetProperty<float>("FarPlaneDistance"), main.Camera.FarPlaneDistance)); WorldFactory.AddState(result.GetListProperty<Map.CellState>("AdditionalMaterials").ToArray()); result.Add(new CommandBinding(result.Delete, delegate() { WorldFactory.RemoveState(result.GetListProperty<Map.CellState>("AdditionalMaterials").ToArray()); })); // Zone management Zone currentZone = null; result.Add(new CommandBinding<Entity>(main.EntityAdded, delegate(Entity e) { if (!e.CannotSuspend) processEntity(e, currentZone, getActiveBoundingBoxes(main.Camera, currentZone), main.Camera.Position, main.Camera.FarPlaneDistance); })); IEnumerable<NonAxisAlignedBoundingBox> boxes = getActiveBoundingBoxes(main.Camera, currentZone); Vector3 cameraPosition = main.Camera.Position; float suspendDistance = main.Camera.FarPlaneDistance; foreach (Entity e in main.Entities) { if (!e.CannotSuspend) processEntity(e, currentZone, boxes, cameraPosition, suspendDistance); } result.Add("ProcessMap", new Command<Map> { Action = delegate(Map map) { processMap(map, boxes); } }); Property<float> reverbAmount = result.GetProperty<float>("ReverbAmount"); Property<float> reverbSize = result.GetProperty<float>("ReverbSize"); Sound.ReverbSettings(main, reverbAmount, reverbSize); Vector3 lastUpdatedCameraPosition = new Vector3(float.MinValue); bool lastFrameUpdated = false; Action<Zone> updateZones = delegate(Zone newZone) { currentZone = newZone; if (newZone != null) Sound.ReverbSettings(main, newZone.ReverbAmount, newZone.ReverbSize); else Sound.ReverbSettings(main, reverbAmount, reverbSize); boxes = getActiveBoundingBoxes(main.Camera, newZone); cameraPosition = main.Camera.Position; suspendDistance = main.Camera.FarPlaneDistance; foreach (Entity e in main.Entities) { if (!e.CannotSuspend) processEntity(e, newZone, boxes, cameraPosition, suspendDistance); } lastUpdatedCameraPosition = main.Camera.Position; }; result.Add("UpdateZones", new Command { Action = delegate() { updateZones(Zone.Get(main.Camera.Position)); }, }); Updater update = new Updater { delegate(float dt) { // Update every other frame if (lastFrameUpdated) { lastFrameUpdated = false; return; } lastFrameUpdated = true; Zone newZone = Zone.Get(main.Camera.Position); if (newZone != currentZone || (newZone == null && (main.Camera.Position - lastUpdatedCameraPosition).Length() > 10.0f)) updateZones(newZone); } }; update.EnabledInEditMode.Value = true; result.Add(update); this.SetMain(result, main); WorldFactory.instance = result; }
private object Read(Entity entity) { var refIdProperty = _columnInfo.Property as IRefIdProperty; if (refIdProperty != null) { object id = refIdProperty.Nullable ? entity.GetRefNullableId(refIdProperty) : entity.GetRefId(refIdProperty); return id; } var value = entity.GetProperty(_columnInfo.Property); return value; }
public override void Bind(Entity result, Main main, bool creating = false) { result.CannotSuspend = true; Transform transform = result.Get<Transform>(); ModelInstance model = result.Get<ModelInstance>(); model.Add(new Binding<Matrix>(model.Transform, transform.Matrix)); model.Scale.Value = Vector3.Zero; Property<bool> scale = result.GetProperty<bool>("Scale"); Property<Vector3> start = result.GetProperty<Vector3>("StartPosition"); start.Set = delegate(Vector3 value) { start.InternalValue = value; transform.Position.Value = value; }; Property<Matrix> startOrientation = result.GetProperty<Matrix>("StartOrientation"); Vector3 startEuler = Vector3.Zero; startOrientation.Set = delegate(Matrix value) { startOrientation.InternalValue = value; startEuler = Quaternion.CreateFromRotationMatrix(startOrientation).ToEuler(); transform.Orientation.Value = value; }; Property<Entity.Handle> map = result.GetProperty<Entity.Handle>("TargetMap"); Property<Map.Coordinate> coord = result.GetProperty<Map.Coordinate>("TargetCoord"); Property<int> stateId = result.GetProperty<int>("TargetCellStateID"); Property<float> totalLifetime = result.GetProperty<float>("TotalLifetime"); Property<float> lifetime = result.GetProperty<float>("Lifetime"); Updater update = null; update = new Updater { delegate(float dt) { lifetime.Value += dt; float blend = lifetime / totalLifetime; if (map.Value.Target == null || !map.Value.Target.Active) { result.Delete.Execute(); return; } Map m = map.Value.Target.Get<Map>(); if (blend > 1.0f) { if (stateId != 0) { Map.Coordinate c = coord; bool foundAdjacentCell = false; foreach (Direction dir in DirectionExtensions.Directions) { Map.Coordinate adjacent = c.Move(dir); if (m[adjacent].ID != 0) { foundAdjacentCell = true; break; } } if (foundAdjacentCell) { bool foundConflict = false; Vector3 absolutePosition = m.GetAbsolutePosition(c); foreach (Map m2 in Map.ActivePhysicsMaps) { if (m2 != m && m2[absolutePosition].Permanent) { foundConflict = true; break; } } if (!foundConflict) { Map.CellState state = m[coord]; if (state.Permanent) foundConflict = true; else { if (state.ID != 0) m.Empty(coord); m.Fill(coord, WorldFactory.States[stateId]); m.Regenerate(); Sound.PlayCue(main, "BuildBlock", transform.Position, 1.0f, 0.06f); result.Delete.Execute(); return; } } } // For one reason or another, we can't fill the cell // Animate nicely into oblivion update.Delete.Execute(); result.Add(new Animation ( new Animation.Vector3MoveTo(model.Scale, Vector3.Zero, 1.0f), new Animation.Execute(result.Delete) )); } } else { if (scale) model.Scale.Value = new Vector3(blend); else model.Scale.Value = new Vector3(1.0f); Matrix finalOrientation = m.Transform; finalOrientation.Translation = Vector3.Zero; Vector3 finalEuler = Quaternion.CreateFromRotationMatrix(finalOrientation).ToEuler(); finalEuler = Vector3.Lerp(startEuler, finalEuler, blend); transform.Orientation.Value = Matrix.CreateFromYawPitchRoll(finalEuler.X, finalEuler.Y, finalEuler.Z); Vector3 finalPosition = m.GetAbsolutePosition(coord); float distance = (finalPosition - start).Length() * 0.1f * Math.Max(0.0f, 0.5f - Math.Abs(blend - 0.5f)); transform.Position.Value = Vector3.Lerp(start, finalPosition, blend) + new Vector3((float)Math.Sin(blend * Math.PI) * distance); } }, }; result.Add(update); BlockEntry entry = new BlockEntry(); result.Add(new NotifyBinding(delegate() { if (entry.Map != null) this.animatingBlocks.Remove(entry); Entity m = map.Value.Target; entry.Map = m != null ? m.Get<Map>() : null; if (entry.Map != null) { entry.Coordinate = coord; this.animatingBlocks[entry] = true; } }, map, coord, stateId)); result.Add(new CommandBinding(result.Delete, delegate() { if (entry.Map != null) this.animatingBlocks.Remove(entry); entry.Map = null; })); this.SetMain(result, main); IBinding offsetBinding = null; model.Add(new NotifyBinding(delegate() { if (offsetBinding != null) model.Remove(offsetBinding); offsetBinding = new Binding<Vector3>(model.GetVector3Parameter("Offset"), result.GetProperty<Vector3>("Offset")); model.Add(offsetBinding); }, model.FullInstanceKey)); }
public override void Bind(Entity result, Main main, bool creating = false) { Factory.Get<DynamicMapFactory>().Bind(result, main, creating); Transform transform = result.Get<Transform>(); DynamicMap map = result.Get<DynamicMap>(); Sound zombieSound = new Sound(); zombieSound.Serialize = false; zombieSound.Cue.Value = "Zombie"; result.Add("ZombieSound", zombieSound); Property<bool> playerVisible = new Property<bool> { Value = false, Editable = false, Serialize = false }; result.Add("PlayerVisible", playerVisible); zombieSound.Add(new Binding<Vector3>(zombieSound.Position, transform.Position)); zombieSound.Add(new Binding<Vector3>(zombieSound.Velocity, map.LinearVelocity)); Property<float> damage = result.GetProperty<float>("Damage"); map.Add(new CommandBinding<Collidable, ContactCollection>(map.Collided, delegate(Collidable collidable, ContactCollection contact) { if (result.Active && collidable is EntityCollidable) { if (((EntityCollidable)collidable).Entity.Tag is Player) { Player player = (Player)((EntityCollidable)collidable).Entity.Tag; player.Health.Value -= damage; } } })); Property<float> zombieSoundPitch = zombieSound.GetProperty("Pitch"); Property<float> visibilityCheckInterval = result.GetProperty<float>("VisibilityCheckInterval"); Property<float> torqueMultiplier = result.GetProperty<float>("TorqueMultiplier"); Property<float> maxSpeed = result.GetProperty<float>("MaxSpeed"); Property<float> playerPositionMemoryTime = result.GetProperty<float>("PlayerPositionMemoryTime"); float timeSinceLastSpottedPlayer = playerPositionMemoryTime; float timeSinceLastVisibilityCheck = 0.0f; result.Add(new Updater { delegate(float dt) { if (!result.Active) return; Entity player = PlayerFactory.Instance; if (player != null) { Vector3 playerPosition = player.Get<Transform>().Position.Value; Vector3 rayDirection = playerPosition - transform.Position; float playerDistance = rayDirection.Length(); rayDirection /= playerDistance; timeSinceLastVisibilityCheck += dt; if (timeSinceLastVisibilityCheck > visibilityCheckInterval) { Map.GlobalRaycastResult hit = Map.GlobalRaycast(playerPosition + (rayDirection * -3.0f), -rayDirection, playerDistance); if (hit.Map == map) { timeSinceLastSpottedPlayer = 0.0f; playerVisible.Value = true; } timeSinceLastVisibilityCheck = 0.0f; } timeSinceLastSpottedPlayer += dt; if (timeSinceLastSpottedPlayer < playerPositionMemoryTime) { float torque = torqueMultiplier * map.PhysicsEntity.Mass * dt; Vector3 impulse = new Vector3(torque * rayDirection.Z, 0.0f, -torque * rayDirection.X); map.PhysicsEntity.ApplyAngularImpulse(ref impulse); Vector3 velocity = map.PhysicsEntity.AngularVelocity; float speed = velocity.Length(); if (speed > maxSpeed) map.PhysicsEntity.AngularVelocity = velocity * (maxSpeed / speed); map.PhysicsEntity.ActivityInformation.Activate(); zombieSoundPitch.Value = ((speed / maxSpeed) / torque) - 1.0f; } else if (playerVisible) playerVisible.Value = false; } if (timeSinceLastSpottedPlayer > playerPositionMemoryTime && zombieSound.IsPlaying) zombieSound.Stop.Execute(Microsoft.Xna.Framework.Audio.AudioStopOptions.AsAuthored); else if (timeSinceLastSpottedPlayer < playerPositionMemoryTime && !zombieSound.IsPlaying) zombieSound.Play.Execute(); } }); }