public static void Pack(HamTimelineNode node, DataPacker packer) { packer.Pack((byte)node.Type); packer.Pack(node.ID); packer.Pack(node.PreviousNodeIDs.Count); for (int i = 0; i < node.PreviousNodeIDs.Count; ++i) { packer.Pack(node.PreviousNodeIDs[i]); } node.Pack(packer); }
public PlacementNode(HamTimelineNode node, PlacementNode parent) { this.Node = node; this.Parent = parent; if (this.Parent != null) { this.Parent.Children.Add(this); this.Depth = this.Parent.Depth + 1; } else { this.Depth = 0; } this.Children = new List<PlacementNode>(); this.Width = 0; this.WidthOffset = 0; }
public static void Unpack(out HamTimelineNode node, DataUnpacker unpacker) { byte typeByte; unpacker.Unpack(out typeByte); TimelineNodeType type = (TimelineNodeType)typeByte; int id; unpacker.Unpack(out id); int numPrevIDs; unpacker.Unpack(out numPrevIDs); List<int> previousNodeIDs = new List<int>(); for (int i = 0; i < numPrevIDs; ++i) { int prevID; unpacker.Unpack(out prevID); previousNodeIDs.Add(prevID); } switch (type) { case TimelineNodeType.Dialog: node = new HamDialogNode(); break; case TimelineNodeType.Branch: node = new HamBranchNode(); break; case TimelineNodeType.Decision: node = new HamDecisionNode(); break; case TimelineNodeType.Consequence: node = new HamConsequenceNode(); break; default: node = null; return; } node.ID = id; node.Type = type; node.PreviousNodeIDs = previousNodeIDs; node.Unpack(unpacker); }
// Visualization and Editing // Walk up the tree until a dialog node is found public HamDialogNode GetLastDialogNode(HamTimelineNode node) { if (node.Type == TimelineNodeType.Dialog) { return node as HamDialogNode; } for (int i = 0; i < node.PreviousNodeIDs.Count; ++i) { HamDialogNode last = GetLastDialogNode(this.Nodes[node.PreviousNodeIDs[i]]); if (last != null) { return last; } } return null; }
public bool CanDeleteTree(HamTimelineNode node) { return node.ID != this.OriginNodeID; }
public bool CanRemoveCleanly(HamTimelineNode node) { int dependants = 0; List<int> descendants = node.GetDescendantIDs(); if (descendants.Count == 0 && node.ID == this.OriginNodeID) { // If we're deleting the origin node, we need a replacement return false; } for (int i = 0; i < descendants.Count; ++i) { HamTimelineNode d = this.Nodes[descendants[i]]; if (d.UniquelyParented) { dependants += 1; } } return dependants < 2; }
public bool CanLinkCleanly(HamTimelineNode parent, HamTimelineNode child, int i = 0) { int formerChildID = parent.GetDescendant(i); if (formerChildID == InvalidID) { return true; } return (child.GetFreeDescendantSlot() != -1); }
public void LinkNodes(HamTimelineNode parent, HamTimelineNode child, int i = 0) { // Check for previous linkage int formerChildID = parent.GetDescendant(i); if (formerChildID != InvalidID) { HamTimelineNode formerChild = this.Nodes[formerChildID]; // Remove former linkage formerChild.PreviousNodeIDs.Remove(parent.ID); // Set former child's parentage to the new child int slot = child.GetFreeDescendantSlot(); child.SetDescendant(formerChild.ID, slot); formerChild.PreviousNodeIDs.Add(child.ID); } // Set new child's parentage and overwrite parent linkage parent.SetDescendant(child.ID, i); child.PreviousNodeIDs.Add(parent.ID); this.NodeLinkageDirty = true; SanityCheck(); }
public void DeleteNode(HamTimelineNode node) { // It's expected that there is at most 1 uniquely parented descendant // Find the dependant node, removing linkages to the current node in all descendants as we go HamTimelineNode dependant = null; List<int> descendants = node.GetDescendantIDs(); for (int i = 0; i < descendants.Count; ++i) { HamTimelineNode descendant = this.Nodes[descendants[i]]; if (descendant.UniquelyParented) { dependant = descendant; } descendant.PreviousNodeIDs.Remove(node.ID); } // Remove linkages to this node in the parents, linking the parents to the dependant node instead if it exists for (int i = 0; i < node.PreviousNodeIDs.Count; ++i) { HamTimelineNode parent = this.Nodes[node.PreviousNodeIDs[i]]; int pIndex = parent.GetIndexOfDescendant(node.ID); parent.SetDescendant((dependant == null) ? InvalidID : dependant.ID, pIndex); } // If the node we're deleting is the origin, set any descendant (preferably the dependant) as the new origin if (node.ID == this.OriginNodeID) { if (dependant != null) { this.OriginNodeID = dependant.ID; } else { // There is assumed to be at least 1 descendant this.OriginNodeID = descendants[0]; } } // Remove this node completely this.Nodes.Remove(node.ID); this.NodeLinkageDirty = true; SanityCheck(); }
public void DeleteTree(HamTimelineNode node) { Queue<HamTimelineNode> toDelete = new Queue<HamTimelineNode>(); toDelete.Enqueue(node); this.Nodes.Remove(node.ID); while (toDelete.Count > 0) { HamTimelineNode del = toDelete.Dequeue(); // Remove any linkage between this node and its parents for (int i = 0; i < del.PreviousNodeIDs.Count; ++i) { if (!this.Nodes.ContainsKey(del.PreviousNodeIDs[i])) { // This parent already deleted, ignore it and move on continue; } HamTimelineNode parent = this.Nodes[del.PreviousNodeIDs[i]]; int pIndex = parent.GetIndexOfDescendant(del.ID); parent.SetDescendant(InvalidID, pIndex); } // Queue up children List<int> descendants = del.GetDescendantIDs(); for (int i = 0; i < descendants.Count; ++i) { if (!this.Nodes.ContainsKey(descendants[i])) { // This descendant already deleted, ignore it and move on continue; } HamTimelineNode descendant = this.Nodes[descendants[i]]; int validParents = 0; for (int j = 0; j < descendant.PreviousNodeIDs.Count; ++j) { if (this.Nodes.ContainsKey(descendant.PreviousNodeIDs[j]) && descendant.PreviousNodeIDs[j] != del.ID) { validParents += 1; } } descendant.PreviousNodeIDs.Remove(del.ID); if (validParents == 0) { // This node is the only parent of the descendant, it gets deleted as part of the tree toDelete.Enqueue(descendant); this.Nodes.Remove(descendant.ID); } } } this.NodeLinkageDirty = true; SanityCheck(); }
public SelectionContext() { this.Node = null; this.Mode = SelectionMode.SelectNode; this.DescendantIndex = 0; }
private void RightClickNodeContext(HamTimelineNode node, int descendantIndex) { GenericMenu menu = new GenericMenu(); if (descendantIndex != -1) { menu.AddItem( new GUIContent("Link To Selection"), false, (a) => { this.selection.BeginLinking(node, descendantIndex); }, null ); menu.AddItem( new GUIContent("Link To New Dialog"), false, (a) => { HamDialogNode lastDialog = this.activeTimeline.GetLastDialogNode(node); HamTimelineNode newNode = this.activeTimeline.AddDialogNode( lastDialog != null ? lastDialog.SceneID : this.activeTimeline.DefaultSceneID, lastDialog != null ? lastDialog.SpeakerID : this.activeTimeline.NarratorID, "", lastDialog != null ? lastDialog.CharacterIDs : null ); this.activeTimeline.LinkNodes(node, newNode, descendantIndex); }, null ); menu.AddItem( new GUIContent("Link To New Decision"), false, () => { HamTimelineNode newNode = this.activeTimeline.AddDecisionNode(); this.activeTimeline.LinkNodes(node, newNode, descendantIndex); } ); menu.AddItem( new GUIContent("Link To New Branch"), false, () => { HamTimelineNode newNode = this.activeTimeline.AddBranchNode(); this.activeTimeline.LinkNodes(node, newNode, descendantIndex); } ); menu.AddItem( new GUIContent("Link To New Consequence"), false, () => { HamTimelineNode newNode = this.activeTimeline.AddConsequenceNode(); this.activeTimeline.LinkNodes(node, newNode, descendantIndex); } ); } if (this.activeTimeline.CanRemoveCleanly(node)) { menu.AddItem( new GUIContent("Remove Node (Relink Dependants)"), false, () => { this.activeTimeline.DeleteNode(node); } ); } if (this.activeTimeline.CanDeleteTree(node)) { menu.AddItem( new GUIContent("Remove Node (Delete Dependants)"), false, () => { this.activeTimeline.DeleteTree(node); } ); } menu.ShowAsContext(); }
private Vector2 GetOverviewPosition(HamTimelineNode node) { if (this.overviewNodePlacement == null || !this.overviewNodePlacement.ContainsKey(node.ID)) { return Vector2.zero; } Vector2 placement = this.overviewNodePlacement[node.ID]; return new Vector2( placement.x * kNodeSpacingX, placement.y * kNodeSpacingY ); }
public void BeginLinking(HamTimelineNode node, int index = 0) { this.Node = node; this.DescendantIndex = index; this.Mode = SelectionMode.LinkNodes; }
public void ClickedNode(HamTimeline timeline, HamTimelineNode node) { switch (this.Mode) { case SelectionMode.SelectNode: this.Node = node; break; case SelectionMode.LinkNodes: if (this.Node != node) { if (timeline.CanLinkCleanly(this.Node, node, this.DescendantIndex)) { timeline.LinkNodes(this.Node, node, this.DescendantIndex); } else { EditorUtility.DisplayDialog("Node Linking Failed", "No clean way was found to reparent the currently linked node", "OK"); } } this.Mode = SelectionMode.SelectNode; break; } }