mirror of
https://port.numenaute.org/aleajactaest/khanat-client.git
synced 2024-11-09 08:49:05 +00:00
184 lines
5.5 KiB
GDScript
184 lines
5.5 KiB
GDScript
extends Reference
|
|
|
|
var SquareCell = preload("./square_cell.gd")
|
|
|
|
var origin = Vector3( 0.0, 0.0, 0.0 )
|
|
var size = Vector3( 0, 0, 0 )
|
|
var cell_size = Vector3( 1.0, 1.0, 1.0 )
|
|
|
|
var path_cost_default = 10.0
|
|
|
|
var cells_cost = {}
|
|
var obstacles = []
|
|
var barrieres = {}
|
|
|
|
func _init( p_size = Vector2( 100, 100 ), p_origin = Vector3( 0.0, 0.0, 0.0 ) ):
|
|
|
|
self.origin = p_origin
|
|
self.size = p_size
|
|
|
|
func set_size( p_size ):
|
|
self.size = p_size
|
|
|
|
func get_cell_coords( cell ):
|
|
var cell_coords = Vector3()
|
|
if typeof(cell) == TYPE_VECTOR3:
|
|
cell_coords = cell
|
|
else:
|
|
cell_coords = cell.coords
|
|
return cell_coords
|
|
|
|
func get_cell_at( p_position ):
|
|
|
|
var position_local = p_position - self.origin
|
|
|
|
var coords = (position_local / self.cell_size)
|
|
|
|
var cell = SquareCell.new( coords )
|
|
|
|
return cell
|
|
|
|
func get_cell_position( p_cell ):
|
|
var position = (p_cell.coords * self.cell_size) + self.origin
|
|
return position
|
|
|
|
func get_cell_direction( p_direction ):
|
|
if p_direction is SquareCell.DIR_ALL:
|
|
return p_direction
|
|
|
|
var coords = self.get_cell_at( p_direction ).coords
|
|
if coords is SquareCell.DIR_ALL:
|
|
return coords
|
|
|
|
return null
|
|
|
|
|
|
func add_obstacle( cell, cost=0 ):
|
|
self.obstacles.push_back( self.get_cell_coords( cell ) )
|
|
|
|
func remove_obstacle( cell ):
|
|
self.obstacles.erase( self.get_cell_coords( cell ) )
|
|
|
|
func is_obstacle( cell ):
|
|
return ( self.get_cell_coords( cell ) in self.obstacles )
|
|
|
|
func add_barriere( cell, dir ):
|
|
var coords = self.get_cell_coords( cell )
|
|
if not coords in self.barrieres:
|
|
self.barrieres[ coords ] = []
|
|
if not dir in self.barrieres[ coords ]:
|
|
self.barrieres[ coords ].push_back( dir )
|
|
|
|
func remove_barriere( cell, dir ):
|
|
var coords = self.get_cell_coords( cell )
|
|
if coords in self.barrieres and dir in self.barrieres[ coords ]:
|
|
self.barrieres[ coords ].remove( dir )
|
|
|
|
func update_barrieres( p_cell, p_barrieres ):
|
|
var coords = self.get_cell_coords( p_cell )
|
|
self.barrieres[ coords ] = p_barrieres
|
|
|
|
|
|
|
|
func is_barriere( cell, dir ):
|
|
var coords = self.get_cell_coords( cell )
|
|
if not coords in self.barrieres:
|
|
return false
|
|
if not dir in self.barrieres[ coords ]:
|
|
return false
|
|
return true
|
|
|
|
func get_move_cost(p_cell, p_direction):
|
|
var cell_coords = self.get_cell_coords( p_cell )
|
|
|
|
var next_cell = SquareCell.new( cell_coords + p_direction )
|
|
var cost = abs(p_direction.x) + abs(p_direction.y) + abs(p_direction.z)*self.get_cell_cost( next_cell )
|
|
|
|
if self.is_obstacle( cell_coords ):
|
|
return 0
|
|
|
|
if cell_coords in self.barrieres and p_direction in self.barrieres[ cell_coords ]:
|
|
return 0
|
|
|
|
if next_cell.coords in self.barrieres and -p_direction in self.barrieres[ next_cell.coords ]:
|
|
return 0
|
|
|
|
return cost
|
|
|
|
func find_path(start, goal, exceptions=[]):
|
|
|
|
# Light a starry path from the start to the goal, inclusive
|
|
start = start.coords
|
|
goal = goal.coords
|
|
# Make sure all the exceptions are coords
|
|
var exc = []
|
|
for exception in exceptions:
|
|
if exception is SquareCell:
|
|
exc.append(exception.coords)
|
|
exceptions = exc
|
|
# Now we begin the A* search
|
|
var frontier = [self.make_priority_item(start, 0)]
|
|
var came_from = {start: null}
|
|
var cost_so_far = {start: 0}
|
|
while not frontier.empty():
|
|
var current = frontier.pop_front().v
|
|
if current == goal:
|
|
break
|
|
for next_cell in SquareCell.new(current).get_cells_adjacent():
|
|
var next = next_cell.coords
|
|
var next_cost = self.get_move_cost(current, next - current)
|
|
if next == goal and (next in exceptions or get_cell_cost(next) == 0):
|
|
# Our goal is an obstacle, but we're next to it
|
|
# so our work here is done
|
|
came_from[next] = current
|
|
frontier.clear()
|
|
break
|
|
if not next_cost or next in exceptions:
|
|
# We shall not pass
|
|
continue
|
|
next_cost += cost_so_far[current]
|
|
if not next in cost_so_far or next_cost < cost_so_far[next]:
|
|
# New shortest path to that node
|
|
cost_so_far[next] = next_cost
|
|
var priority = next_cost + next_cell.cell_distance_to(goal)
|
|
# Insert into the frontier
|
|
var item = make_priority_item(next, priority)
|
|
var idx = frontier.bsearch_custom(item, self, "comp_priority_item")
|
|
frontier.insert(idx, item)
|
|
came_from[next] = current
|
|
|
|
if not goal in came_from:
|
|
# Not found
|
|
print( "Path not found." )
|
|
return []
|
|
|
|
# Follow the path back where we came_from
|
|
var path = []
|
|
if not (self.get_cell_cost(goal) == 0 or goal in exceptions):
|
|
# We only include the goal if it's traversable
|
|
path.append(SquareCell.new(goal))
|
|
|
|
var current = goal
|
|
while current != start:
|
|
current = came_from[current]
|
|
path.push_front(SquareCell.new(current))
|
|
|
|
return path
|
|
|
|
# Used to make a priority queue out of an array
|
|
func make_priority_item(val, priority):
|
|
return {"v": val, "p": priority}
|
|
func comp_priority_item(a, b):
|
|
return a.p < b.p
|
|
|
|
func get_cell_cost( cell ):
|
|
var cell_coords = self.get_cell_coords( cell )
|
|
|
|
if self.is_obstacle( cell_coords ):
|
|
return 0
|
|
|
|
if cell_coords in self.cells_cost:
|
|
return self.cells_cost[ cell_coords ]
|
|
|
|
return self.path_cost_default
|
|
|