private string GetVariant() { string compoundVariant = null; FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; Vector2Int[] selectedPositions = feature.GetSelectedSections(); foreach (Vector2Int position in selectedPositions) { if (feature.TryGetSection(position, out Section section)) { if (compoundVariant == null) { //first variant found compoundVariant = section.variant; } else if (compoundVariant != section.variant) { //variant names are not uniform return(null); } //variant names are uniform so far, continue } } return(compoundVariant); }
// Check for valid internal links void ValidateInternalLink(FeatureAsset feature, Vector2Int featureAssetPos) { feature.TryGetLink(featureAssetPos, out Link link); Assert.IsNotNull(link, "Internal link expected to exist at position: " + featureAssetPos); Assert.True(link.open, "Link was expected to be open (found closed) at position: " + featureAssetPos); Assert.False(link.external, "Link was expected to be internal (found external) at position: " + featureAssetPos); }
public void CanConvertFromFeature_Square() { // Feature is a 2x2 square. Feature feature = new Feature(); feature.Add(0, 0); feature.Add(1, 0); feature.Add(0, 1); feature.Add(1, 1); FeatureAsset featureAsset = FeatureAsset.FromFeature(feature); // Basic test- are sections present? ValidateSection(featureAsset, new Vector2Int(0, 0)); ValidateSection(featureAsset, new Vector2Int(1, 0)); ValidateSection(featureAsset, new Vector2Int(0, 1)); ValidateSection(featureAsset, new Vector2Int(1, 1)); /* * Layout of the FeatureAsset grid, for reference. * * 3 o | o | o * 2 - r - r - * 1 o | x | o * 0 - r - r - * -1 o | o | o * -1 0 1 2 3 */ ValidateExternalLink(featureAsset, new Vector2Int(0, 3)); // Top ValidateExternalLink(featureAsset, new Vector2Int(2, 3)); ValidateExternalLink(featureAsset, new Vector2Int(-1, 0)); // Left ValidateExternalLink(featureAsset, new Vector2Int(-1, 2)); ValidateExternalLink(featureAsset, new Vector2Int(0, -1)); // Bottom ValidateExternalLink(featureAsset, new Vector2Int(2, -1)); ValidateExternalLink(featureAsset, new Vector2Int(3, 0)); // Right ValidateExternalLink(featureAsset, new Vector2Int(3, 2)); ValidateInternalLink(featureAsset, new Vector2Int(1, 2)); // Top ValidateInternalLink(featureAsset, new Vector2Int(0, 1)); // Left ValidateInternalLink(featureAsset, new Vector2Int(1, 0)); // Bottom ValidateInternalLink(featureAsset, new Vector2Int(2, 1)); // Right ValidateInternalLink(featureAsset, new Vector2Int(1, 1)); // Middle ValidateMissingLink(featureAsset, new Vector2Int(-1, -1)); ValidateMissingLink(featureAsset, new Vector2Int(1, -1)); ValidateMissingLink(featureAsset, new Vector2Int(3, -1)); ValidateMissingLink(featureAsset, new Vector2Int(-1, 1)); //ValidateMissingLink(featureAsset, new Vector2Int(1, 1)); // Middle ValidateMissingLink(featureAsset, new Vector2Int(3, 1)); ValidateMissingLink(featureAsset, new Vector2Int(-1, 3)); ValidateMissingLink(featureAsset, new Vector2Int(1, 3)); ValidateMissingLink(featureAsset, new Vector2Int(3, 3)); }
public void CanConvertFromFeature_Line() { // Feature 1 is a horizontal line. Feature feature1 = new Feature(); feature1.Add(0, 0); feature1.Add(1, 0); feature1.Add(2, 0); FeatureAsset featureAsset1 = FeatureAsset.FromFeature(feature1); // Basic test. Make sure all sections are present in FeatureAsset. // FA uses a 2x grid, so we multiply all inputs by 2 to verify. ValidateSection(featureAsset1, new Vector2Int(0, 0)); ValidateSection(featureAsset1, new Vector2Int(1, 0)); ValidateSection(featureAsset1, new Vector2Int(2, 0)); /* -1012345 * o|o|o|o * -X-X-X- * o|o|o|o */ // Middle ValidateExternalLink(featureAsset1, new Vector2Int(-1, 0)); ValidateInternalLink(featureAsset1, new Vector2Int(1, 0)); // Should be internal ValidateInternalLink(featureAsset1, new Vector2Int(3, 0)); // Should be internal ValidateExternalLink(featureAsset1, new Vector2Int(5, 0)); // Top row ValidateExternalLink(featureAsset1, new Vector2Int(0, 1)); ValidateExternalLink(featureAsset1, new Vector2Int(2, 1)); ValidateExternalLink(featureAsset1, new Vector2Int(4, 1)); // Bottom row ValidateExternalLink(featureAsset1, new Vector2Int(0, -1)); ValidateExternalLink(featureAsset1, new Vector2Int(2, -1)); ValidateExternalLink(featureAsset1, new Vector2Int(4, -1)); // Invalid links ValidateMissingLink(featureAsset1, new Vector2Int(-1, 1)); ValidateMissingLink(featureAsset1, new Vector2Int(1, 1)); ValidateMissingLink(featureAsset1, new Vector2Int(3, 1)); ValidateMissingLink(featureAsset1, new Vector2Int(5, 1)); ValidateMissingLink(featureAsset1, new Vector2Int(-1, -1)); ValidateMissingLink(featureAsset1, new Vector2Int(1, -1)); ValidateMissingLink(featureAsset1, new Vector2Int(3, -1)); ValidateMissingLink(featureAsset1, new Vector2Int(5, -1)); }
public void CanConvertFromFeature_Diagonal() { // Feature is a diagonal line. Shouldn't have a link on the diagonal. Feature feature = new Feature(); feature.Add(0, 0); feature.Add(1, 1); FeatureAsset featureAsset = FeatureAsset.FromFeature(feature); ValidateMissingLink(featureAsset, new Vector2Int(1, 1)); // More tests should go here }
public void CanConvertFromFeature_Donut() { // Feature is a 3x3 square with a missing center. Feature feature = new Feature(); feature.Add(0, 0); feature.Add(1, 0); feature.Add(2, 0); feature.Add(0, 1); //feature.Add(1, 1); feature.Add(2, 1); feature.Add(0, 2); feature.Add(1, 2); feature.Add(2, 2); FeatureAsset featureAsset = FeatureAsset.FromFeature(feature); // Basic test- are sections present? //ValidateSection(featureAsset, new Vector2Int(0, 0)); //ValidateSection(featureAsset, new Vector2Int(1, 0)); //ValidateSection(featureAsset, new Vector2Int(0, 1)); //ValidateSection(featureAsset, new Vector2Int(1, 1)); /* * 5 o | o | o | o * 4 - r - r - r - * 3 o | o | o | o * 2 - r - o - r - * 1 o | o | o | o * 0 - r - r - r - * -1 o | o | o | o * -1 0 1 2 3 4 5 */ // Inside the donut, we should have external links ValidateExternalLink(featureAsset, new Vector2Int(2, 1)); ValidateExternalLink(featureAsset, new Vector2Int(2, 3)); ValidateExternalLink(featureAsset, new Vector2Int(1, 2)); ValidateExternalLink(featureAsset, new Vector2Int(3, 2)); // More tests should go here }
public void CanConvertToFromFeature() { Feature feature1 = new Feature(); feature1.Add(0, 0); feature1.Add(1, 0); feature1.Add(2, 0); FeatureAsset featureAsset1 = FeatureAsset.FromFeature(feature1); Feature testFeature1 = featureAsset1.ToFeature(); // Basic test. All input positions should be present in output Assert.Contains(new Vector2Int(0, 0), testFeature1.Elements.Keys); Assert.Contains(new Vector2Int(1, 0), testFeature1.Elements.Keys); Assert.Contains(new Vector2Int(2, 0), testFeature1.Elements.Keys); // Basic test. Ensure bounds are unchanged Assert.AreEqual(feature1.MaxX, testFeature1.MaxX); Assert.AreEqual(feature1.MaxY, testFeature1.MaxY); Assert.AreEqual(feature1.MinX, testFeature1.MinX); Assert.AreEqual(feature1.MinY, testFeature1.MinY); // Harder test. Conversion of Feature -> FeatureAsset -> Feature does not obey identity property. // All connections at the end will be present in the original, but additionally, // they will be restricted based on FeatureAsset retention rules. // // Thus, we test FeatureAsset retention rules separately, and ensure that // FeatureAfter.connections & FeatureBefore.connections == FeatureAfter.connections // to be sure that no connections are ever dropped. foreach (KeyValuePair <Vector2Int, Labrys.Generation.Section> kvp in testFeature1.Elements) { Labrys.Generation.Section orig = feature1.Elements[kvp.Key]; Labrys.Generation.Section conv = kvp.Value; // Some connections may be lost. Ensure no new ones are added. Assert.AreEqual(orig.externalConnections & conv.externalConnections, conv.externalConnections); Assert.AreEqual(orig.internalConnections & conv.internalConnections, conv.internalConnections); // Make sure variants are unchanged. Assert.AreEqual(orig.GetVariant(), conv.GetVariant()); } }
private IField[] GetFields() { Dictionary <string, AggSectionField> aggregatedFields = new Dictionary <string, AggSectionField>(); FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; int count = 0; // Create aggregate field objects containing all fields in the enumerated sections, common or not feature.ForAllSelectedSections((Section s) => { count++; foreach (SectionField sf in s) { if (aggregatedFields.TryGetValue(sf.Name, out AggSectionField aggregate)) { aggregate.Fields.Add(sf); } else { aggregatedFields.Add(sf.Name, new AggSectionField() { Fields = { sf } }); } } }); // If an aggregate contains a field for every enumerated section, then add it to the return list; // this can be done because fields are guaranteed to be unique per section List <AggSectionField> commonAggregates = new List <AggSectionField>(); foreach (AggSectionField aggregate in aggregatedFields.Values) { if (aggregate.Fields.Count == count) { commonAggregates.Add(aggregate); } } return(commonAggregates.ToArray()); }
private void DrawObjects() { FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; if (feature == null) { return; } Vector2 screenPosition = Vector2.zero; Rect bounds = new Rect(screenPosition, GetScaledTileSize()); // Draw Sections foreach (KeyValuePair <Vector2Int, Section> section in feature.GetSections()) { screenPosition = GridToScreenPos(section.Key); bounds.position = screenPosition; bounds.center = screenPosition; Color temp = GUI.color; GUI.color = feature.IsSelected(section.Key) ? sectionSelectedColor : Color.white; GUI.Box(bounds, section.Value.variant); GUI.color = temp; } // Draw Links foreach (KeyValuePair <Vector2Int, Link> link in feature.GetLinks()) { screenPosition = GridToScreenPos(link.Key); Handles.color = link.Value.open ? linkOpenColor : linkClosedColor; Handles.BeginGUI(); Handles.DrawSolidDisc(new Vector3(screenPosition.x, screenPosition.y), Vector3.forward, scale * LINK_SIZE); if (link.Value.external) { Handles.color = linkExternalColor; Handles.DrawSolidArc(new Vector3(screenPosition.x, screenPosition.y), Vector3.forward, Vector3.left, 180f, scale * LINK_SIZE); } Handles.EndGUI(); } }
public override bool HandleEvent(Event e) { if (tileEditorPanel.HandleEvent(e)) { return(true); } switch (e.type) { case EventType.MouseDown: if (IsPrimaryControl(e)) { //start selection box selectionRect.position = e.mousePosition; e.Use(); selectionMode = SELECT_MODE_SELECT; return(true); } else if (IsSecondaryControl(e)) { //start selection box selectionRect.position = e.mousePosition; e.Use(); selectionMode = SELECT_MODE_DESELECT; return(true); } break; case EventType.MouseDrag: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { //update selection box selectionRect.max = e.mousePosition; e.Use(); return(true); } break; case EventType.MouseUp: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { //select all tiles within selection box string description; UnityAction action; Vector2Int[] positions = EditorGrid.GetInstance().RectToGridPositions(selectionRect, true); FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; if (selectionMode == SELECT_MODE_SELECT) { description = "Select section(s)"; action = () => { if (!e.shift) { //not additive feature.DeselectAllSections(); } foreach (Vector2Int gp in positions) { //additive feature.SelectSection(gp); } }; } else if (selectionMode == SELECT_MODE_DESELECT) { description = "Deselect section(s)"; action = () => { if (!e.shift) { //not additive feature.DeselectAllSections(); } else { //additive foreach (Vector2Int gp in positions) { feature.DeselectSection(gp); } } }; } else { description = "ERROR"; action = null; } ChangeAsset(feature, description, action); selectionRect = new Rect(); selectionMode = SELECT_MODE_NONE; e.Use(); return(true); } break; } return(false); }
// Check for invalid links void ValidateMissingLink(FeatureAsset feature, Vector2Int featureAssetPos) { feature.TryGetLink(featureAssetPos, out Link link); Assert.IsNull(link, "Link expected to be missing at position: " + featureAssetPos); }
// Confirms a given section exists at a position. private void ValidateSection(FeatureAsset feature, Vector2Int featurePos) { feature.TryGetSection(featurePos * 2, out Labrys.FeatureEditor.Section section); Assert.IsNotNull(section, "Section expected to exist at position: " + (featurePos * 2)); }
public override bool HandleEvent(Event e) { switch (e.type) { case EventType.MouseDown: settingExternal = (IsPrimaryControl(e) || IsSecondaryControl(e)) && e.control; goto case EventType.MouseDrag; case EventType.MouseDrag: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { Vector2Int position = EditorGrid.GetInstance().ScreenToGridPos(e.mousePosition); if (FeatureEditorWindow.GetInstance().Feature.HasLinkAt(position)) { manipPositions.Add(position); if (settingExternal) { previewColor = Color.yellow; } else if (IsPrimaryControl(e)) { previewColor = Color.green; } else if (IsSecondaryControl(e)) { previewColor = Color.red; } return(true); } } break; case EventType.MouseUp: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; string description; UnityAction action; bool targetState = IsPrimaryControl(e) ? true : IsSecondaryControl(e) ? false : false; if (settingExternal) { description = targetState ? "Set connection(s) to external" : "Set connections(s) to internal"; action = () => { foreach (Vector2Int position in manipPositions) { if (feature.TryGetLink(position, out Link link)) { link.external = targetState; } } }; } else { description = targetState ? "Set connection(s) to open" : "Set connections(s) to closed"; action = () => { foreach (Vector2Int position in manipPositions) { if (feature.TryGetLink(position, out Link link)) { link.open = targetState; } } }; } ChangeAsset(feature, description, action); e.Use(); manipPositions.Clear(); return(true); } break; } return(false); }
public override bool HandleEvent(Event e) { switch (e.type) { case EventType.MouseDown: case EventType.MouseDrag: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { Vector2Int position = EditorGrid.GetInstance().ScreenToGridPos(e.mousePosition, true); bool posHasTile = FeatureEditorWindow.GetInstance().Feature.HasSectionAt(position); if ((IsPrimaryControl(e) && !posHasTile) || (IsSecondaryControl(e) && posHasTile)) { manipPositions.Add(position); if (IsPrimaryControl(e)) { previewColor = Color.green; } else if (IsSecondaryControl(e)) { previewColor = Color.red; } return(true); } } break; case EventType.MouseUp: if (IsPrimaryControl(e) || IsSecondaryControl(e)) { //attempt to place a tile in accumpulated positions FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; string description; UnityAction action; //add sections if (IsPrimaryControl(e)) { description = "Add section(s)"; action = () => { foreach (Vector2Int position in manipPositions) { FeatureEditorWindow.GetInstance().Feature.AddSection(position); } }; } //remove sections else if (IsSecondaryControl(e)) { description = "Remove section(s)"; action = () => { foreach (Vector2Int position in manipPositions) { FeatureEditorWindow.GetInstance().Feature.RemoveSection(position); } }; } else { return(false); } ChangeAsset(feature, description, action); e.Use(); manipPositions.Clear(); return(true); } break; } return(false); }
public override bool HandleEvent(Event e) { const float padding = 5; const float defControlHeight = 20; float maxWidth = bounds.width - (padding * 2); float currY = bounds.yMin + padding; float minX = bounds.xMin + padding; FeatureAsset feature = FeatureEditorWindow.GetInstance().Feature; //selected section number Rect selSecCountRect = new Rect(minX, currY, maxWidth, defControlHeight); int selectionCount = feature.GetSelectedCount(); GUI.Box(selSecCountRect, selectionCount + (selectionCount == 1 ? " tile selected" : " tiles selected")); currY += selSecCountRect.height + padding; //variant field label Rect variantFieldLabelRect = new Rect(minX, currY, maxWidth / 4, defControlHeight); GUI.Box(variantFieldLabelRect, "Variant"); //variant field select button Rect variantSelButRect = new Rect(bounds.xMax - padding - (maxWidth / 4), currY, maxWidth / 4, defControlHeight); if (GUI.Button(variantSelButRect, "Select")) { string fullPath = EditorUtility.OpenFolderPanel("Select Variant Folder", lastSelectedFolder != "" ? lastSelectedFolder : Application.dataPath, ""); string[] splitFullPath = fullPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); variant = splitFullPath[splitFullPath.Length - 1]; SetVariant(variant); //save parent of selected folder lastSelectedFolder = fullPath.Substring(0, Mathf.Max( fullPath.LastIndexOf(Path.AltDirectorySeparatorChar), fullPath.LastIndexOf(Path.DirectorySeparatorChar))); } //variant field text box Rect variantTextRect = new Rect(minX + (maxWidth / 4), currY, maxWidth / 2, defControlHeight); string initialVariant = GetVariant(); if (initialVariant == null) { variant = MIXED_VALUES; } else { variant = initialVariant; } string newVariant = EditorGUI.TextField(variantTextRect, variant); if (newVariant != MIXED_VALUES && newVariant != variant) { Undo.RegisterCompleteObjectUndo(feature, "Change variant"); feature.ForAllSelectedSections((Section s) => { s.variant = variant = newVariant; }); EditorUtility.SetDirty(feature); } currY += defControlHeight + padding; //kvp add new Rect addNewKvpRect = new Rect(minX, currY, maxWidth, defControlHeight); if (GUI.Button(addNewKvpRect, "Add New Field")) { TextDialogWindow.Create((string text) => { Undo.RegisterCompleteObjectUndo(feature, "Add New Field"); feature.ForAllSelectedSections((Section s) => { s.AddField(text); }); EditorUtility.SetDirty(feature); }); } currY += defControlHeight + padding; //kvp scroll view IField[] fields = GetFields(); const float fieldRectHeight = 20; Rect scrollViewRect = new Rect(minX, currY, maxWidth, bounds.yMax - currY); Rect scrollBoundsRect = new Rect(scrollViewRect); scrollBoundsRect.height = (fieldRectHeight + padding) * fields.Length; scrollBoundsRect.width -= 20; kvpFieldScrollPos = GUI.BeginScrollView(scrollViewRect, kvpFieldScrollPos, scrollBoundsRect, false, true); Rect fieldRect = new Rect(scrollBoundsRect); float startY = fieldRect.y; fieldRect.height = fieldRectHeight; for (int i = 0; i < fields.Length; i++) { fieldRect.y = startY + (20 + padding) * i; Rect fieldRectPartial = new Rect(fieldRect); fieldRectPartial.width = fieldRect.width / 2; GUI.Box(fieldRectPartial, fields[i].Name); fieldRectPartial.x += fieldRect.width / 2; fieldRectPartial.width /= 2; string initalValue = fields[i].Value; string modifiValue = EditorGUI.TextField(fieldRectPartial, initalValue); if (modifiValue != initalValue) { Undo.RegisterCompleteObjectUndo(feature, $"Modified {fields[i].Name}"); feature.ForAllSelectedSections((Section s) => { s.SetField(fields[i].Name, modifiValue); }); EditorUtility.SetDirty(feature); } fieldRectPartial.x += fieldRect.width / 4; if (GUI.Button(fieldRectPartial, "Remove")) { feature.ForAllSelectedSections((Section s) => { s.RemoveField(fields[i].Name); }); } } GUI.EndScrollView(handleScrollWheel: true); return(bounds.Contains(e.mousePosition)); }