protected override async Task Invoke(MapDocument document, CommandParameters parameters) { using (var qf = new QuickForm(Title) { UseShortcutKeys = true }.TextBox("ObjectID", ObjectID).OkCancel(OK, Cancel)) { qf.ClientSize = new Size(230, qf.ClientSize.Height); if (await qf.ShowDialogAsync() != DialogResult.OK) { return; } if (!long.TryParse(qf.String("ObjectID"), out var id)) { return; } var obj = document.Map.Root.FindByID(id); if (obj == null) { return; } var tran = new Transaction( new Deselect(document.Selection), new Select(obj) ); await MapDocumentOperation.Perform(document, tran); var box = obj.BoundingBox; await Task.WhenAll( Oy.Publish("MapDocument:Viewport:Focus3D", box), Oy.Publish("MapDocument:Viewport:Focus2D", box) ); } }
protected override async Task Invoke(MapDocument document, CommandParameters parameters) { using (var qf = new QuickForm(Title) { UseShortcutKeys = true }.TextBox("X", "X", "0").TextBox("Y", "Y", "0").TextBox("Z", "Z", "0").OkCancel(OK, Cancel)) { qf.ClientSize = new Size(180, qf.ClientSize.Height); if (await qf.ShowDialogAsync() != DialogResult.OK) { return; } if (!Decimal.TryParse(qf.String("X"), out var x)) { return; } if (!Decimal.TryParse(qf.String("Y"), out var y)) { return; } if (!Decimal.TryParse(qf.String("Z"), out var z)) { return; } var coordinate = new Vector3((float)x, (float)y, (float)z); var box = new Box(coordinate - (Vector3.One * 10), coordinate + (Vector3.One * 10)); await Task.WhenAll( Oy.Publish("MapDocument:Viewport:Focus3D", box), Oy.Publish("MapDocument:Viewport:Focus2D", box) ); } }
protected override async Task Invoke(MapDocument document, CommandParameters parameters) { var objects = document.Selection.OfType <Solid>().ToList(); // Prompt the user for the wall width. If more than 1 solid is selected, show a little warning notice. var qf = new QuickForm(PromptTitle) { UseShortcutKeys = true }; if (objects.Count > 1) { qf.Label(String.Format(WarningMessage, objects.Count)); } qf.NumericUpDown("Width", PromptWallWidth, -1024, 1024, 0, 32); qf.OkCancel(OK, Cancel); if (await qf.ShowDialogAsync() != DialogResult.OK) { return; } var width = (float)qf.Decimal("Width"); var ops = new List <IOperation>(); foreach (var obj in objects) { var split = false; var solid = obj; // Make a scaled version of the solid for the "inside" of the hollowed solid var origin = solid.BoundingBox.Center; var current = obj.BoundingBox.Dimensions; var target = current - new Vector3(width, width, width) * 2; // Double the width to take from both sides // Ensure we don't have any invalid target sizes if (target.X < 1) { target.X = 1; } if (target.Y < 1) { target.Y = 1; } if (target.Z < 1) { target.Z = 1; } // Clone and scale the solid var scale = Vector3.Divide(target, current); var carver = (Solid)solid.Clone(); carver.Transform(Matrix4x4.CreateTranslation(-origin) * Matrix4x4.CreateScale(scale) * Matrix4x4.CreateTranslation(origin)); // For a negative width, we want the original solid to be the inside instead if (width < 0) { var temp = carver; carver = solid; solid = temp; } // Carve the outside solid with the inside solid foreach (var plane in carver.Faces.Select(x => x.Plane)) { // Split solid by plane Solid back, front; try { if (!solid.Split(document.Map.NumberGenerator, plane, out back, out front)) { continue; } } catch { // We're not too fussy about over-complicated carving, just get out if we've broken it. break; } split = true; if (front != null) { // Retain the front solid if (solid.IsSelected) { front.IsSelected = true; } ops.Add(new Attach(obj.Hierarchy.Parent.ID, front)); } if (back == null || !back.IsValid()) { break; } // Use the back solid as the new clipping target if (solid.IsSelected) { back.IsSelected = true; } solid = back; } if (!split) { continue; } ops.Add(new Detatch(obj.Hierarchy.Parent.ID, obj)); } if (ops.Any()) { await MapDocumentOperation.Perform(document, new Transaction(ops)); } }
protected override async Task Invoke(MapDocument document, CommandParameters parameters) { var existingEntities = document.Selection.OfType <Entity>().ToList(); Entity existing = null; var confirmed = false; if (existingEntities.Count == 0) { // No entities selected, just create it straight up confirmed = true; } else if (existingEntities.Count == 1) { // One entity selected, user chooses to merge or create a new entity using ( var qf = new QuickForm(EntitySelectedTitle) { Width = 400 } .Label(String.Format(OneEntitySelectedMessage, existingEntities[0].EntityData?.Name)) .DialogButtons( (KeepExisting, DialogResult.Yes), (CreateNew, DialogResult.No), (Cancel, DialogResult.Cancel) ) ) { var result = await qf.ShowDialogAsync(); if (result == DialogResult.Yes) { existing = existingEntities[0]; } confirmed = result != DialogResult.Cancel; } } else if (existingEntities.Count > 1) { // Multiple entities selected, user chooses which one to keep using ( var qf = new QuickForm(EntitySelectedTitle) { Width = 400 } .Label(MultipleEntitiesSelectedMessage) .ComboBox("Entity", "", existingEntities.Select(x => new EntityContainer { Entity = x })) .OkCancel(OK, Cancel) ) { var result = await qf.ShowDialogAsync(); if (result == DialogResult.OK) { existing = (qf.Object("Entity") as EntityContainer)?.Entity; } confirmed = result != DialogResult.Cancel; } } if (!confirmed) { return; } var ops = new List <IOperation>(); var gameData = await document.Environment.GetGameData(); var def = document.Environment.DefaultBrushEntity; var defaultEntityClass = ( from g in gameData.Classes where g.ClassType == ClassType.Solid orderby String.Equals(g.Name, def, StringComparison.InvariantCultureIgnoreCase) ? 0 : 1, String.Equals(g.Name, "trigger_once", StringComparison.InvariantCultureIgnoreCase) ? 0 : 1, g.Description.ToLower() select g ).FirstOrDefault() ?? new GameDataObject("trigger_once", "", ClassType.Solid); if (existing == null) { // If the entity doesn't exist we need to create it existing = new Entity(document.Map.NumberGenerator.Next("MapObject")) { Data = { new EntityData { Name = defaultEntityClass.Name }, new ObjectColor(Colour.GetDefaultEntityColour()) } }; ops.Add(new Attach(document.Map.Root.ID, existing)); } else { // If the entity is a descendant of the selection, it would cause havok ops.Add(new Detatch(existing.Hierarchy.Parent.ID, existing)); ops.Add(new Attach(document.Map.Root.ID, existing)); } // Any other entities in the selection should be destroyed foreach (var entity in existingEntities.Where(x => !ReferenceEquals(x, existing))) { var children = entity.Hierarchy.Where(x => !(x is Entity)).ToList(); ops.Add(new Detatch(entity.ID, children)); ops.Add(new Attach(existing.ID, children)); ops.Add(new Detatch(entity.Hierarchy.Parent.ID, entity)); } // All other parents should be added to the entity foreach (var obj in document.Selection.GetSelectedParents().Except(existingEntities)) { ops.Add(new Detatch(obj.Hierarchy.Parent.ID, obj)); ops.Add(new Attach(existing.ID, obj)); } if (ops.Any()) { ops.Add(new Select(document.Selection.Except(existingEntities).Union(new[] { existing }))); await MapDocumentOperation.Perform(document, new Transaction(ops)); await Oy.Publish("Context:Add", new ContextInfo("BspEditor:ObjectProperties")); } }