Spider enemy that uses pathfinding
parent
d947671e8a
commit
808fdd9a00
|
@ -1,2 +1,4 @@
|
|||
time_speed = 0
|
||||
max_forceloaded_blocks = 10000
|
||||
active_object_send_range_blocks = 8
|
||||
active_block_range = 16
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
local path = minetest.get_modpath('enemy') .. '/'
|
||||
|
||||
dofile(path .. 'script/enemy/spider.lua')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,78 @@
|
|||
minetest.register_entity('enemy:spider', {
|
||||
visual = 'mesh',
|
||||
visual_size = vector.new(14, 14, 14),
|
||||
textures = { 'enemy_spider.png' },
|
||||
mesh = 'enemy_spider.b3d',
|
||||
physical = true,
|
||||
collisionbox = { -0.25, -0.5, -0.25, 0.25, 0.25, 0.25 },
|
||||
on_activate = function(self, static_data)
|
||||
self.object:set_acceleration({x = 0, y = -10, z = 0})
|
||||
self.object:set_animation({ x = 0, y = 15 }, 30, 0, true)
|
||||
end,
|
||||
on_punch = function(self)
|
||||
self.object:remove()
|
||||
end,
|
||||
on_step = function(self, dtime, collision)
|
||||
if not self.path then
|
||||
if not navigation.graph then return end
|
||||
|
||||
self.path = navigation.find_path(
|
||||
navigation.graph, vector.round(self.object:get_pos()), navigation.graph.player_spawn)
|
||||
|
||||
if not self.path then return end
|
||||
self.path_index = #self.path - 1
|
||||
|
||||
for _, node in ipairs(self.path) do
|
||||
minetest.add_particle({
|
||||
pos = node,
|
||||
size = 16,
|
||||
expirationtime = 3,
|
||||
texture = 'navigation_indicator.png'
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local pos_2d = self.object:get_pos()
|
||||
pos_2d.y = 0
|
||||
|
||||
local target = self.path[self.path_index]
|
||||
if target == nil then return end
|
||||
|
||||
local target_2d = { x = target.x, y = 0, z = target.z }
|
||||
|
||||
local target_dist = vector.distance(pos_2d, target_2d)
|
||||
|
||||
if target_dist < 0.2 then
|
||||
self.path_index = self.path_index - 1
|
||||
if self.path_index < 1 then
|
||||
self.path = nil
|
||||
self.path_index = nil
|
||||
return
|
||||
end
|
||||
else
|
||||
local current_dir = self.object:get_rotation().y + math.pi / 2
|
||||
local target_dir_vec = vector.direction(pos_2d, target_2d)
|
||||
local target_dir = math.atan2(target_dir_vec.z, target_dir_vec.x)
|
||||
|
||||
local to_dir = current_dir + (target_dir - current_dir) * 0.1
|
||||
local to_dir_vec = { x = math.cos(to_dir), y = 0, z = math.sin(to_dir) }
|
||||
|
||||
self.object:set_rotation({ x = 0, y = to_dir - math.pi / 2, z = 0 })
|
||||
self.object:set_velocity({x = target_dir_vec.x * 3, y = self.object:get_velocity().y, z = target_dir_vec.z * 3})
|
||||
|
||||
if collision.touching_ground and target.y > self.object:get_pos().y then
|
||||
self.object:set_velocity({x = self.object:get_velocity().x, y = 5, z = self.object:get_velocity().z })
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand('spawnenemy', {
|
||||
params = '<type>',
|
||||
description = 'Spawns an enemy',
|
||||
func = function(name, type)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
minetest.add_entity(vector.round(player:get_pos()), 'enemy:spider', type)
|
||||
end
|
||||
})
|
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
|
@ -6,10 +6,6 @@ minetest.register_entity('machine:mining_drill_entity', {
|
|||
pointable = false,
|
||||
on_activate = function(self, static_data)
|
||||
self.node_pos = (minetest.deserialize(static_data) or {}).node_pos
|
||||
if not self.node_pos then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
minetest.after(math.random() * 10, function() self.object:set_animation({ x = 0, y = 375 }, 30, 0, true) end)
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
_G['pathfinding'] = {}
|
||||
_G['navigation'] = {}
|
||||
|
||||
dofile(minetest.get_modpath('pathfinding') .. '/script/load.lua')
|
||||
dofile(minetest.get_modpath('pathfinding') .. '/script/astar.lua')
|
||||
dofile(minetest.get_modpath('pathfinding') .. '/script/nodes.lua')
|
||||
dofile(minetest.get_modpath('pathfinding') .. '/script/graph.lua')
|
||||
dofile(minetest.get_modpath('navigation') .. '/script/load.lua')
|
||||
dofile(minetest.get_modpath('navigation') .. '/script/astar.lua')
|
||||
dofile(minetest.get_modpath('navigation') .. '/script/nodes.lua')
|
||||
dofile(minetest.get_modpath('navigation') .. '/script/graph.lua')
|
||||
|
||||
local graph = nil
|
||||
navigation.graph = nil
|
||||
local spawn_pos = { x = 9, y = 2, z = -36 }
|
||||
local map_min = { x = -85, y = -38, z = -156 }
|
||||
local map_size = { x = 320, y = 160, z = 320 }
|
||||
|
||||
function init_graph()
|
||||
local res, graph_duration = pathfinding.build_graph(spawn_pos)
|
||||
graph = res
|
||||
minetest.chat_send_all('Graphed ' .. graph.count .. ' nodes in ' .. graph_duration .. ' ms.')
|
||||
local res, graph_duration = navigation.build_graph(spawn_pos)
|
||||
navigation.graph = res
|
||||
minetest.chat_send_all('Graphed ' .. navigation.graph.count .. ' nodes in ' .. graph_duration .. ' ms.')
|
||||
end
|
||||
|
||||
minetest.register_chatcommand('refresh_graph', {
|
||||
params = '',
|
||||
description = 'Graphs pathfinding nodes',
|
||||
description = 'Graphs navigation nodes',
|
||||
func = function(name)
|
||||
init_graph()
|
||||
end
|
||||
|
@ -28,9 +28,16 @@ minetest.register_chatcommand('test_paths', {
|
|||
params = '',
|
||||
description = 'Graphs paths from enemy spawns to player spawn',
|
||||
func = function(name)
|
||||
for i, spawn in ipairs(graph.enemy_spawns) do
|
||||
if navigation.graph == nil then
|
||||
minetest.chat_send_all('Graph not initialized')
|
||||
return
|
||||
end
|
||||
|
||||
minetest.chat_send_all('Graphing paths.')
|
||||
|
||||
for i, spawn in ipairs(navigation.graph.enemy_spawns) do
|
||||
local start_time = minetest.get_us_time()
|
||||
local path = pathfinding.find_path(graph, spawn, graph.player_spawn, i + 4)
|
||||
local path = navigation.find_path(navigation.graph, spawn, navigation.graph.player_spawn, i + 4)
|
||||
local duration = math.floor((minetest.get_us_time() - start_time) / 1000)
|
||||
|
||||
if path then
|
||||
|
@ -39,7 +46,7 @@ minetest.register_chatcommand('test_paths', {
|
|||
pos = node,
|
||||
size = 16,
|
||||
expirationtime = 3,
|
||||
texture = 'pathfinding_indicator.png'
|
||||
texture = 'navigation_indicator.png'
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -52,7 +59,7 @@ minetest.register_chatcommand('test_paths', {
|
|||
})
|
||||
|
||||
minetest.after(1, function()
|
||||
pathfinding.load_area(map_min, vector.add(map_min, map_size), function(_, loaded, forceload_duration)
|
||||
navigation.load_area(map_min, vector.add(map_min, map_size), function(_, loaded, forceload_duration)
|
||||
minetest.chat_send_all('Force loaded ' .. loaded .. ' blocks in ' .. forceload_duration .. 'ms.')
|
||||
init_graph()
|
||||
end)
|
|
@ -8,7 +8,7 @@
|
|||
-- @returns the path from the start to the end, or nil if no path was found.
|
||||
--
|
||||
|
||||
function pathfinding.find_path(graph, from, to)
|
||||
function navigation.find_path(graph, from, to)
|
||||
local to_pos_str = minetest.pos_to_string(to)
|
||||
local closed = {}
|
||||
local open = {}
|
||||
|
@ -69,15 +69,15 @@ function pathfinding.find_path(graph, from, to)
|
|||
break
|
||||
end
|
||||
|
||||
for adj, cost in pairs(pathfinding.adjacent_list) do
|
||||
for adj, cost in pairs(navigation.adjacent_list) do
|
||||
local adj_pos = { x = lowest.pos.x + adj.x, y = lowest.pos.y + adj.y, z = lowest.pos.z + adj.z }
|
||||
local adj_pos_str = minetest.pos_to_string(adj_pos)
|
||||
|
||||
local score = get_node_score(adj_pos)
|
||||
if score ~= nil and closed[adj_pos_str] == nil then
|
||||
local existing = open[adj_pos_str]
|
||||
if existing == nil or lowest.g + cost <= existing.g then
|
||||
add_node_to_open(adj_pos, lowest, cost, adj_pos_str)
|
||||
if existing == nil or lowest.g + cost + score <= existing.g then
|
||||
add_node_to_open(adj_pos, lowest, cost + score, adj_pos_str)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
-- Positions to be considered 'adjacent' for graph generation and pathfinding.
|
||||
pathfinding.adjacent_list = {
|
||||
-- Positions to be considered 'adjacent' for graph generation and navigation.
|
||||
navigation.adjacent_list = {
|
||||
[{ x = 1, y = -1, z = 0 }] = 1,
|
||||
[{ x = -1, y = -1, z = 0 }] = 1,
|
||||
[{ x = 0, y = -1, z = 1 }] = 1,
|
||||
|
@ -18,21 +18,8 @@ pathfinding.adjacent_list = {
|
|||
[{ x = 0, y = 1, z = -1 }] = 1
|
||||
}
|
||||
|
||||
-- Nodes to be considered valid map nodes.
|
||||
local map_nodes = {
|
||||
['pathfinding:navigation'] = true,
|
||||
['pathfinding:navigation_hidden'] = true,
|
||||
['pathfinding:player_spawn'] = true,
|
||||
['pathfinding:player_spawn_hidden'] = true,
|
||||
['pathfinding:enemy_spawn'] = true,
|
||||
['pathfinding:enemy_spawn_hidden'] = true
|
||||
}
|
||||
|
||||
-- Nodes to be considered enemy spawn points
|
||||
local enemy_spawn_nodes = {
|
||||
['pathfinding:enemy_spawn'] = true,
|
||||
['pathfinding:enemy_spawn_hidden'] = true
|
||||
}
|
||||
local BASE_COST = 10
|
||||
local MAGNET_STRENGTH = 10
|
||||
|
||||
--
|
||||
-- Builds the map graph by recursively scanning the map for map nodes.
|
||||
|
@ -42,11 +29,31 @@ local enemy_spawn_nodes = {
|
|||
-- @returns the graph of the map, and the time to generate it.
|
||||
--
|
||||
|
||||
function pathfinding.build_graph(start)
|
||||
function navigation.build_graph(start)
|
||||
local scan_nodes = {}
|
||||
local magnet_nodes = {}
|
||||
local enemy_spawn_nodes = {}
|
||||
|
||||
for name, def in pairs(minetest.registered_nodes) do
|
||||
if def.groups['nav_traversable'] then
|
||||
scan_nodes[name] = true
|
||||
|
||||
if def._navigation.magnet ~= 0 then
|
||||
magnet_nodes[name] = def._navigation.magnet
|
||||
end
|
||||
|
||||
if def._navigation.spawn == 'enemy' then
|
||||
enemy_spawn_nodes[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local start_time = minetest.get_us_time()
|
||||
local scanned = { minetest.pos_to_string(start) }
|
||||
local to_scan = { start }
|
||||
|
||||
local magnet_positions = {}
|
||||
|
||||
local graph = {
|
||||
|
||||
-- The player's spawn point vector.
|
||||
|
@ -70,22 +77,49 @@ function pathfinding.build_graph(start)
|
|||
|
||||
if not graph.nodes[pos.y] then graph.nodes[pos.y] = {} end
|
||||
if not graph.nodes[pos.y][pos.x] then graph.nodes[pos.y][pos.x] = {} end
|
||||
if not graph.nodes[pos.y][pos.x][pos.z] then graph.nodes[pos.y][pos.x][pos.z] = 1 end
|
||||
if not graph.nodes[pos.y][pos.x][pos.z] then graph.nodes[pos.y][pos.x][pos.z] = BASE_COST end
|
||||
|
||||
for adj, _ in pairs(pathfinding.adjacent_list) do
|
||||
for adj, _ in pairs(navigation.adjacent_list) do
|
||||
local adj_pos = { x = pos.x + adj.x, y = pos.y + adj.y, z = pos.z + adj.z }
|
||||
local adj_pos_str = minetest.pos_to_string(adj_pos)
|
||||
|
||||
if not scanned[adj_pos_str] then
|
||||
local node = minetest.get_node(adj_pos)
|
||||
|
||||
if map_nodes[node.name] then
|
||||
if scan_nodes[node.name] then
|
||||
table.insert(to_scan, adj_pos)
|
||||
scanned[adj_pos_str] = true
|
||||
|
||||
if enemy_spawn_nodes[node.name] then
|
||||
table.insert(graph.enemy_spawns, adj_pos)
|
||||
end
|
||||
|
||||
if magnet_nodes[node.name] then
|
||||
table.insert(magnet_positions, adj_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, pos in ipairs(magnet_positions) do
|
||||
local val = magnet_nodes[minetest.get_node(pos).name]
|
||||
local radius = math.abs(val)
|
||||
|
||||
for x = pos.x - radius, pos.x + radius do
|
||||
for y = pos.y - radius, pos.y + radius do
|
||||
for z = pos.z - radius, pos.z + radius do
|
||||
local strength = math.max(1 - (vector.distance(pos, { x = x, y = y, z = z }) / radius), 0) *
|
||||
(val < 0 and -1 or 1)
|
||||
if strength ~= 0 then
|
||||
local res_cost = BASE_COST - strength * MAGNET_STRENGTH
|
||||
if graph.nodes[y] and graph.nodes[y][x] then
|
||||
local cur_value = graph.nodes[y][x][z]
|
||||
if cur_value and ((strength > 0 and cur_value > res_cost) or (strength < 0 and cur_value < res_cost)) then
|
||||
graph.nodes[y][x][z] = res_cost
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,7 +21,7 @@ end
|
|||
-- @param callback - The callback to call when the area is emerged.
|
||||
--
|
||||
|
||||
function pathfinding.load_area(min_pos, max_pos, cb)
|
||||
function navigation.load_area(min_pos, max_pos, cb)
|
||||
local start_time = minetest.get_us_time()
|
||||
|
||||
local function free()
|
|
@ -0,0 +1,159 @@
|
|||
-- Whether or not the navigation nodes are visible.
|
||||
local nav_visible = false
|
||||
|
||||
--
|
||||
-- Registers an invisible navigation node that defines the navmesh of the map.
|
||||
-- You can toggle the visibility and targeting of the node with /toggle_paths.
|
||||
-- The properties below determine the functionality of the node.
|
||||
--
|
||||
-- @param def - The definition of the node, with the following keys:
|
||||
-- - name: The name of the node.
|
||||
-- - color: The color of the node when paths are toggled on.
|
||||
-- - traversable: Whether or not the node is traversable by enemies.
|
||||
-- - placeable: Whether or not items can be placed on this node.
|
||||
-- - collidable: Whether or not the player collides with the node.
|
||||
-- If true, functions as a barrier.
|
||||
-- - magnet: If the node functions as an attractive or repulsive magnet to navigation.
|
||||
-- A positive number defines an attractive magnet of the radius supplied.
|
||||
-- A negative number defines a repulsive magnet of the absolute value of the radius supplied.
|
||||
-- - spawn: The type of spawnpoint this node is
|
||||
-- nil, 'player', 'enemy'
|
||||
--
|
||||
|
||||
function register_nav_node(def)
|
||||
local navigation = {
|
||||
placeable = def.placeable,
|
||||
traversable = def.traversable,
|
||||
magnet = def.magnet,
|
||||
spawn = def.spawn
|
||||
}
|
||||
|
||||
minetest.register_node('navigation:' .. def.name, {
|
||||
description = def.name,
|
||||
drawtype = 'glasslike_framed',
|
||||
tiles = {
|
||||
'navigation_indicator_frame.png^[multiply:' .. def.color,
|
||||
'navigation_indicator.png^[multiply:' .. def.color
|
||||
},
|
||||
walkable = def.collidable or false,
|
||||
paramtype = 'light',
|
||||
sunlight_propagates = true,
|
||||
drop = '',
|
||||
groups = {
|
||||
nav_node = 1,
|
||||
nav_traversable = def.traversable and 1 or 0,
|
||||
nav_visible = 1,
|
||||
creative_dig = 1
|
||||
},
|
||||
_navigation = navigation,
|
||||
on_place = function(stack, player, target)
|
||||
local pos = target.above
|
||||
if nav_visible then minetest.set_node(pos, { name = 'navigation:' .. def.name })
|
||||
else minetest.set_node(pos, { name = 'navigation:' .. def.name .. '_hidden' }) end
|
||||
return stack
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node('navigation:' .. def.name .. '_hidden', {
|
||||
description = def.name,
|
||||
drawtype = 'airlike',
|
||||
walkable = def.collidable or false,
|
||||
pointable = false,
|
||||
paramtype = 'light',
|
||||
sunlight_propagates = true,
|
||||
drop = '',
|
||||
groups = {
|
||||
nav_node = 1,
|
||||
nav_traversable = def.traversable and 1 or 0,
|
||||
nav_hidden = 1,
|
||||
creative_dig = 1,
|
||||
not_in_creative_inventory = 1
|
||||
},
|
||||
_navigation = navigation
|
||||
})
|
||||
end
|
||||
|
||||
--
|
||||
-- Toggles the visibility of the navigation nodes
|
||||
-- using active block modifiers and commands.
|
||||
--
|
||||
|
||||
minetest.register_abm({
|
||||
label = 'Make navigation nodes visible',
|
||||
nodenames = { 'group:nav_hidden' },
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
min_y = -150,
|
||||
max_y = 150,
|
||||
action = function(pos, node)
|
||||
if not nav_visible then return end
|
||||
local node_name = node.name:gsub('_hidden', '')
|
||||
minetest.set_node(pos, { name = node_name })
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
label = 'Making navigation nodes hidden',
|
||||
nodenames = { 'group:nav_visible' },
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
min_y = -150,
|
||||
max_y = 150,
|
||||
action = function(pos, node)
|
||||
if nav_visible then return end
|
||||
local node_name = node.name .. '_hidden'
|
||||
minetest.set_node(pos, { name = node_name })
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand('toggle_nav', {
|
||||
description = 'Toggle nav node visibility',
|
||||
func = function() nav_visible = not nav_visible end
|
||||
})
|
||||
|
||||
--
|
||||
-- Register the nav nodes.
|
||||
--
|
||||
|
||||
register_nav_node({
|
||||
name = 'nav',
|
||||
color = '#5CD9FF',
|
||||
traversable = true,
|
||||
placeable = true
|
||||
})
|
||||
|
||||
register_nav_node({
|
||||
name = 'nav_positive_magnet',
|
||||
color = '#3DFF7E',
|
||||
traversable = true,
|
||||
placeable = true,
|
||||
magnet = 5
|
||||
})
|
||||
|
||||
register_nav_node({
|
||||
name = 'nav_negative_magnet',
|
||||
color = '#F56642',
|
||||
traversable = true,
|
||||
placeable = true,
|
||||
magnet = -5
|
||||
})
|
||||
|
||||
register_nav_node({
|
||||
name = 'barrier',
|
||||
color = '#FF33A7',
|
||||
collidable = true
|
||||
})
|
||||
|
||||
register_nav_node({
|
||||
name = 'player_spawn',
|
||||
color = '#FFED47',
|
||||
traversable = true,
|
||||
spawn = 'player'
|
||||
})
|
||||
|
||||
register_nav_node({
|
||||
name = 'enemy_spawn',
|
||||
color = '#C53DFF',
|
||||
traversable = true,
|
||||
spawn = 'enemy'
|
||||
})
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
@ -1,84 +0,0 @@
|
|||
local nodes_visible = false
|
||||
|
||||
function register_pathfinding_node(name, color, def)
|
||||
minetest.register_node('pathfinding:' .. name, table.merge({
|
||||
description = name,
|
||||
drawtype = 'glasslike_framed',
|
||||
tiles = { 'pathfinding_indicator_frame.png^[multiply:' .. color, 'pathfinding_indicator.png^[multiply:' .. color },
|
||||
walkable = false,
|
||||
paramtype = 'light',
|
||||
sunlight_propagates = true,
|
||||
drop = '',
|
||||
groups = {
|
||||
pathfinding = 1,
|
||||
pathfinding_visible = 1,
|
||||
['pathfinding_' .. name] = 1,
|
||||
creative_dig = 1
|
||||
},
|
||||
on_place = function(stack, player, target)
|
||||
local pos = target.above
|
||||
if nodes_visible then minetest.set_node(pos, { name = 'pathfinding:' .. name })
|
||||
else minetest.set_node(pos, { name = 'pathfinding:' .. name .. '_hidden' }) end
|
||||
return stack
|
||||
end
|
||||
}, def or {}))
|
||||
|
||||
minetest.register_node('pathfinding:' .. name .. '_hidden', table.merge({
|
||||
description = name,
|
||||
drawtype = 'airlike',
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
paramtype = 'light',
|
||||
sunlight_propagates = true,
|
||||
drop = '',
|
||||
groups = {
|
||||
pathfinding = 1,
|
||||
pathfinding_hidden = 1,
|
||||
['pathfinding_' .. name] = 1,
|
||||
creative_dig = 1,
|
||||
not_in_creative_inventory = 1
|
||||
}
|
||||
}, def))
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = 'Making pathfinding nodes visible',
|
||||
nodenames = { 'group:pathfinding_hidden' },
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
min_y = -300,
|
||||
max_y = 300,
|
||||
action = function(pos, node)
|
||||
if not nodes_visible then return end
|
||||
local node_name = node.name:gsub('_hidden', '')
|
||||
minetest.set_node(pos, { name = node_name })
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
label = 'Making pathfinding nodes hidden',
|
||||
nodenames = { 'group:pathfinding_visible' },
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
min_y = -300,
|
||||
max_y = 300,
|
||||
action = function(pos, node)
|
||||
if nodes_visible then return end
|
||||
local node_name = node.name .. '_hidden'
|
||||
minetest.set_node(pos, { name = node_name })
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand('toggle_paths', {
|
||||
params = '',
|
||||
description = 'Toggles pathfinding node visibility',
|
||||
func = function()
|
||||
nodes_visible = not nodes_visible
|
||||
end
|
||||
})
|
||||
|
||||
register_pathfinding_node('navigation', '#5CD9FF')
|
||||
register_pathfinding_node('navigation_magnet', '#3DFF7E')
|
||||
register_pathfinding_node('barrier', '#FF33A7', { walkable = true })
|
||||
register_pathfinding_node('player_spawn', '#FFED47')
|
||||
register_pathfinding_node('enemy_spawn', '#C53DFF')
|
Binary file not shown.
Loading…
Reference in New Issue