Esempio n. 1
0
        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)
                    );
            }
        }
Esempio n. 2
0
        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)
                    );
            }
        }
Esempio n. 3
0
        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));
            }
        }
Esempio n. 4
0
        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"));
            }
        }