summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/TurnBased/BlockManager.cs
blob: 0a725e41f6c9d8d68fa9aa72f4062a508ad6e0c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
using UnityEngine;
using System.Collections.Generic;

namespace Pathfinding {
	using Pathfinding.Util;

	/// <summary>
	/// Manager for blocker scripts such as SingleNodeBlocker.
	///
	/// This is part of the turn based utilities. It can be used for
	/// any game, but it is primarily intended for turn based games.
	///
	/// See: TurnBasedAI
	/// See: turnbased (view in online documentation for working links)
	/// See: traversal_provider (view in online documentation for working links)
	/// </summary>
	[HelpURL("https://arongranberg.com/astar/documentation/stable/blockmanager.html")]
	public class BlockManager : VersionedMonoBehaviour {
		/// <summary>Contains info on which SingleNodeBlocker objects have blocked a particular node</summary>
		Dictionary<GraphNode, List<SingleNodeBlocker> > blocked = new Dictionary<GraphNode, List<SingleNodeBlocker> >();

		public enum BlockMode {
			/// <summary>All blockers except those in the TraversalProvider.selector list will block</summary>
			AllExceptSelector,
			/// <summary>Only elements in the TraversalProvider.selector list will block</summary>
			OnlySelector
		}

		/// <summary>Blocks nodes according to a BlockManager</summary>
		public class TraversalProvider : ITraversalProvider {
			/// <summary>Holds information about which nodes are occupied</summary>
			readonly BlockManager blockManager;

			/// <summary>Affects which nodes are considered blocked</summary>
			public BlockMode mode { get; private set; }

			/// <summary>
			/// Blockers for this path.
			/// The effect depends on <see cref="mode"/>.
			///
			/// Note that having a large selector has a performance cost.
			///
			/// See: mode
			/// </summary>
			readonly List<SingleNodeBlocker> selector;

			public TraversalProvider (BlockManager blockManager, BlockMode mode, List<SingleNodeBlocker> selector) {
				if (blockManager == null) throw new System.ArgumentNullException("blockManager");
				if (selector == null) throw new System.ArgumentNullException("selector");

				this.blockManager = blockManager;
				this.mode = mode;
				this.selector = selector;
			}

			#region ITraversalProvider implementation

			public bool CanTraverse (Path path, GraphNode node) {
				// This first IF is the default implementation that is used when no traversal provider is used
				if (!node.Walkable || (path != null && (path.enabledTags >> (int)node.Tag & 0x1) == 0)) {
					return false;
				} else if (mode == BlockMode.OnlySelector) {
					return !blockManager.NodeContainsAnyOf(node, selector);
				} else {
					// assume mode == BlockMode.AllExceptSelector
					return !blockManager.NodeContainsAnyExcept(node, selector);
				}
			}

			public bool CanTraverse (Path path, GraphNode from, GraphNode to) {
				return CanTraverse(path, to);
			}

			public uint GetTraversalCost (Path path, GraphNode node) {
				// Same as default implementation
				return path.GetTagPenalty((int)node.Tag) + node.Penalty;
			}

			// This can be omitted in Unity 2021.3 and newer because a default implementation (returning true) can be used there.
			public bool filterDiagonalGridConnections {
				get {
					return true;
				}
			}

			#endregion
		}

		void Start () {
			if (!AstarPath.active)
				throw new System.Exception("No AstarPath object in the scene");
		}

		/// <summary>True if the node contains any blocker which is included in the selector list</summary>
		public bool NodeContainsAnyOf (GraphNode node, List<SingleNodeBlocker> selector) {
			List<SingleNodeBlocker> blockersInNode;

			if (!blocked.TryGetValue(node, out blockersInNode)) {
				return false;
			}

			for (int i = 0; i < blockersInNode.Count; i++) {
				var inNode = blockersInNode[i];
				for (int j = 0; j < selector.Count; j++) {
					// Need to use ReferenceEquals because this code may be called from a separate thread
					// and the equality comparison that Unity provides is not thread safe
					if (System.Object.ReferenceEquals(inNode, selector[j])) {
						return true;
					}
				}
			}
			return false;
		}

		/// <summary>True if the node contains any blocker which is not included in the selector list</summary>
		public bool NodeContainsAnyExcept (GraphNode node, List<SingleNodeBlocker> selector) {
			List<SingleNodeBlocker> blockersInNode;

			if (!blocked.TryGetValue(node, out blockersInNode)) {
				return false;
			}

			for (int i = 0; i < blockersInNode.Count; i++) {
				var inNode = blockersInNode[i];
				bool found = false;
				for (int j = 0; j < selector.Count; j++) {
					// Need to use ReferenceEquals because this code may be called from a separate thread
					// and the equality comparison that Unity provides is not thread safe
					if (System.Object.ReferenceEquals(inNode, selector[j])) {
						found = true;
						break;
					}
				}
				if (!found) return true;
			}
			return false;
		}

		/// <summary>
		/// Register blocker as being present at the specified node.
		/// Calling this method multiple times will add multiple instances of the blocker to the node.
		///
		/// Note: The node will not be blocked immediately. Instead the pathfinding
		/// threads will be paused and then the update will be applied. It is however
		/// guaranteed to be applied before the next path request is started.
		/// </summary>
		public void InternalBlock (GraphNode node, SingleNodeBlocker blocker) {
			AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
				List<SingleNodeBlocker> blockersInNode;
				if (!blocked.TryGetValue(node, out blockersInNode)) {
					blockersInNode = blocked[node] = ListPool<SingleNodeBlocker>.Claim();
				}

				blockersInNode.Add(blocker);
			}));
		}

		/// <summary>
		/// Remove blocker from the specified node.
		/// Will only remove a single instance, calling this method multiple
		/// times will remove multiple instances of the blocker from the node.
		///
		/// Note: The node will not be unblocked immediately. Instead the pathfinding
		/// threads will be paused and then the update will be applied. It is however
		/// guaranteed to be applied before the next path request is started.
		/// </summary>
		public void InternalUnblock (GraphNode node, SingleNodeBlocker blocker) {
			AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
				List<SingleNodeBlocker> blockersInNode;
				if (blocked.TryGetValue(node, out blockersInNode)) {
					blockersInNode.Remove(blocker);

					if (blockersInNode.Count == 0) {
						blocked.Remove(node);
						ListPool<SingleNodeBlocker>.Release(ref blockersInNode);
					}
				}
			}));
		}
	}
}