From 4b5d01c2a79dc8084041f1b3af6bc9a17342d0c2 Mon Sep 17 00:00:00 2001 From: Katja Date: Mon, 20 May 2024 16:31:11 +0200 Subject: [PATCH] Initial commit. --- .gitignore | 5 + .gitmodules | 4 + SConstruct | 32 +++ build.cmd | 1 + demo/.gitattributes | 2 + demo/.gitignore | 5 + demo/bin/MultilevelAStarExt.gdextension | 13 + demo/bin/multilevel_astar.gd | 63 +++++ demo/icon.svg | 1 + demo/icon.svg.import | 37 +++ demo/main.gd | 74 +++++ demo/main.tscn | 84 ++++++ demo/markers/marker.png | Bin 0 -> 251 bytes demo/markers/marker.png.import | 34 +++ demo/markers/marker.tscn | 9 + demo/markers/unit.png | Bin 0 -> 156 bytes demo/markers/unit.png.import | 34 +++ demo/markers/unit.tscn | 9 + demo/project.godot | 20 ++ demo/tiles/socerb.png | Bin 0 -> 36800 bytes demo/tiles/socerb.png.import | 34 +++ demo/tiles/socerb.tres | 204 ++++++++++++++ godot-cpp | 1 + src/MultilevelAStarEx.cpp | 346 ++++++++++++++++++++++++ src/MultilevelAStarEx.h | 85 ++++++ src/main.cpp | 36 +++ term.cmd | 2 + 27 files changed, 1135 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 SConstruct create mode 100644 build.cmd create mode 100644 demo/.gitattributes create mode 100644 demo/.gitignore create mode 100644 demo/bin/MultilevelAStarExt.gdextension create mode 100644 demo/bin/multilevel_astar.gd create mode 100644 demo/icon.svg create mode 100644 demo/icon.svg.import create mode 100644 demo/main.gd create mode 100644 demo/main.tscn create mode 100644 demo/markers/marker.png create mode 100644 demo/markers/marker.png.import create mode 100644 demo/markers/marker.tscn create mode 100644 demo/markers/unit.png create mode 100644 demo/markers/unit.png.import create mode 100644 demo/markers/unit.tscn create mode 100644 demo/project.godot create mode 100644 demo/tiles/socerb.png create mode 100644 demo/tiles/socerb.png.import create mode 100644 demo/tiles/socerb.tres create mode 160000 godot-cpp create mode 100644 src/MultilevelAStarEx.cpp create mode 100644 src/MultilevelAStarEx.h create mode 100644 src/main.cpp create mode 100644 term.cmd diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54b4bd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.obj +*.exp +*.lib +.sconsign.dblite +*.dll diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ad039fc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "godot-cpp"] + path = godot-cpp + url = https://github.com/godotengine/godot-cpp + branch = 4.2 diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..74d9e6d --- /dev/null +++ b/SConstruct @@ -0,0 +1,32 @@ +#!/usr/bin/env python +import os +import sys + +env = SConscript("godot-cpp/SConstruct") + +# For reference: +# - CCFLAGS are compilation flags shared between C and C++ +# - CFLAGS are for C-specific compilation flags +# - CXXFLAGS are for C++-specific compilation flags +# - CPPFLAGS are for pre-processor flags +# - CPPDEFINES are for pre-processor defines +# - LINKFLAGS are for linking flags + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=["src/"]) +sources = Glob("src/*.cpp") + +if env["platform"] == "macos": + library = env.SharedLibrary( + "demo/bin/libMultilevelAStarExt.{}.{}.framework/libMultilevelAStarExt.{}.{}".format( + env["platform"], env["target"], env["platform"], env["target"] + ), + source=sources, + ) +else: + library = env.SharedLibrary( + "demo/bin/libMultilevelAStarExt{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), + source=sources, + ) + +Default(library) diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..8f98c94 --- /dev/null +++ b/build.cmd @@ -0,0 +1 @@ +scons platform=windows diff --git a/demo/.gitattributes b/demo/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/demo/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..78e73a1 --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1,5 @@ +# Godot 4+ specific ignores +.godot/ + +# Misc +*.tmp diff --git a/demo/bin/MultilevelAStarExt.gdextension b/demo/bin/MultilevelAStarExt.gdextension new file mode 100644 index 0000000..fe82ee4 --- /dev/null +++ b/demo/bin/MultilevelAStarExt.gdextension @@ -0,0 +1,13 @@ +[configuration] + +entry_symbol = "my_astar_ext_init" +compatibility_minimum = "4.2" + +[libraries] + +macos.debug = "res://bin/libMultilevelAStarExt.macos.template_debug.framework" +macos.release = "res://bin/libMultilevelAStarExt.macos.template_release.framework" +windows.debug.x86_64 = "res://bin/libMultilevelAStarExt.windows.template_debug.x86_64.dll" +windows.release.x86_64 = "res://bin/libMultilevelAStarExt.windows.template_release.x86_64.dll" +linux.debug.x86_64 = "res://bin/libMultilevelAStarExt.linux.template_debug.x86_64.so" +linux.release.x86_64 = "res://bin/libMultilevelAStarExt.linux.template_release.x86_64.so" diff --git a/demo/bin/multilevel_astar.gd b/demo/bin/multilevel_astar.gd new file mode 100644 index 0000000..bf2b73b --- /dev/null +++ b/demo/bin/multilevel_astar.gd @@ -0,0 +1,63 @@ +extends RefCounted +class_name MultilevelAStar + +# tile custom data "type": +# 0 => blocked +# 1(+) => can move to same number +# -1(-) => can move to same, one larger or one smaller as long as they're both negative +# => can move to and from its absolute value and one larger than its absolute value + +# private variables start with an _ as per the GDScript style guide +var _used_rect: Rect2i +var _astar: MultilevelAStarEx + + +func _init(map: TileMap): + _used_rect = map.get_used_rect() + + # assert stuff here because the extension's assertions just crash without a message + assert(_used_rect.get_area() > 0) + + _astar = MultilevelAStarEx.new() + _astar.init(_used_rect) + + for layer in map.get_layers_count(): + var cells := map.get_used_cells(layer) + for cell in cells: + var tile_data := map.get_cell_tile_data(layer, cell) + if tile_data != null: + var type = tile_data.get_custom_data("type") + assert(type is int) + _astar.set_terrain(cell, type) + + +func is_unit(point: Vector2i) -> bool: + assert(_used_rect.has_point(point)) + return _astar.get_unit(point) + + +func set_unit(point: Vector2i, solid: bool = true) -> void: + assert(_used_rect.has_point(point)) + _astar.set_unit(point, solid) + + +func get_terrain(point: Vector2i) -> MultilevelAStarEx.TerrainType: + assert(_used_rect.has_point(point)) + return _astar.get_terrain(point) + + +func set_terrain(point: Vector2i, type: MultilevelAStarEx.TerrainType) -> void: + assert(_used_rect.has_point(point)) + _astar.set_terrain(point, type) + + +func find_path(from: Vector2i, to: Vector2i, return_closest: bool = false) -> Array[Vector2i]: + assert(_used_rect.has_point(from)) + assert(_used_rect.has_point(to)) + + var res = _astar.find_path(from, to, return_closest) # vrne Variant: null ali Array[Vector2i] + + if res != null: + return res + else: + return [] diff --git a/demo/icon.svg b/demo/icon.svg new file mode 100644 index 0000000..3fe4f4a --- /dev/null +++ b/demo/icon.svg @@ -0,0 +1 @@ + diff --git a/demo/icon.svg.import b/demo/icon.svg.import new file mode 100644 index 0000000..fd9ae2c --- /dev/null +++ b/demo/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cpupav84hxj1u" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/demo/main.gd b/demo/main.gd new file mode 100644 index 0000000..11366a7 --- /dev/null +++ b/demo/main.gd @@ -0,0 +1,74 @@ +extends Node2D + + +const CELL_SIZE: int = 32 + +@onready var map: TileMap = $TileMap +@onready var marker_from: Node2D = $MarkerFrom +@onready var marker_to: Node2D = $MarkerTo +@onready var path_line: Line2D = $PathLine +@onready var check_box: CheckBox = $CheckBox + +var astar: MultilevelAStar +var used_rect: Rect2i + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + # mapa začne na -1, -1. tukaj premaknem da se vidi vse na mapi + # (saj verjetno obstaja boljši način ma ga ne poznam - mogoče kakšna kamera) + position = Vector2i.ONE * CELL_SIZE + + used_rect = map.get_used_rect() + print(used_rect) + + astar = MultilevelAStar.new(map) + + mark_units() + + marker_from.modulate = Color.html("#ff0000ff") + marker_to.modulate = Color.html("#00ff00ff") + + marker_from.position = Vector2i(-1, -1) * CELL_SIZE + marker_to.position = (used_rect.position + used_rect.size - Vector2i.ONE) * CELL_SIZE + + find_path() + + +func mark_units() -> void: + for unit in $Units.get_tree().get_nodes_in_group("unit"): + astar.set_unit(unit.position / CELL_SIZE) + + +func _unhandled_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.pressed: + var cell := map.local_to_map(map.get_local_mouse_position()) + if map.get_used_rect().has_point(cell): + if event.button_index == 1: + marker_to.position = Vector2(cell.x * CELL_SIZE, cell.y * CELL_SIZE) + find_path(); + elif event.button_index == 2: + marker_from.position = Vector2(cell.x * CELL_SIZE, cell.y * CELL_SIZE) + find_path(); + + +func find_path() -> void: + path_line.clear_points() + + var from: Vector2i = Vector2i(marker_from.position / CELL_SIZE) + var to: Vector2i = Vector2i(marker_to.position / CELL_SIZE) + + var arr = astar.find_path(from, to, check_box.button_pressed) # vrne Variant: null ali Array + #print(arr) + + if arr.size() > 0: + # add new path + path_line.add_point(from * CELL_SIZE + Vector2i(16, 16)) + for vec in arr: + #print("%s, %s" % [vec.x, vec.y]) + path_line.add_point(vec * CELL_SIZE + Vector2i(16, 16)) + + +func _on_check_box_pressed() -> void: + find_path() diff --git a/demo/main.tscn b/demo/main.tscn new file mode 100644 index 0000000..1492fcd --- /dev/null +++ b/demo/main.tscn @@ -0,0 +1,84 @@ +[gd_scene load_steps=5 format=3 uid="uid://1bko0mopl8r7"] + +[ext_resource type="Script" path="res://main.gd" id="1_ub2iy"] +[ext_resource type="TileSet" uid="uid://dsc2e3ohtmv01" path="res://tiles/socerb.tres" id="2_c6ah4"] +[ext_resource type="PackedScene" uid="uid://cnrhrho63n4n2" path="res://markers/marker.tscn" id="3_iwmvr"] +[ext_resource type="PackedScene" uid="uid://cj34xrleexqf6" path="res://markers/unit.tscn" id="4_rc8be"] + +[node name="Main" type="Node2D"] +script = ExtResource("1_ub2iy") + +[node name="TileMap" type="TileMap" parent="."] +tile_set = ExtResource("2_c6ah4") +format = 2 +layer_0/name = "Ground" +layer_0/tile_data = PackedInt32Array(65536, 0, 0, 131072, 0, 0, 196608, 0, 0, 262144, 0, 0, 327680, 0, 0, 393216, 0, 0, 458752, 0, 0, 524288, 0, 0, 589824, 0, 0, 655360, 0, 0, 720896, 0, 0, 786432, 0, 0, 851968, 0, 0, 1, 0, 0, 65537, 0, 0, 131073, 0, 0, 196609, 0, 0, 262145, 0, 0, 327681, 0, 0, 393217, 0, 0, 458753, 0, 0, 524289, 0, 0, 589825, 0, 0, 655361, 0, 0, 720897, 0, 0, 786433, 0, 0, 851969, 0, 0, 2, 0, 0, 65538, 0, 0, 131074, 0, 0, 196610, 0, 0, 262146, 0, 0, 327682, 0, 0, 393218, 0, 0, 458754, 0, 0, 524290, 0, 0, 589826, 0, 0, 655362, 0, 0, 720898, 0, 0, 786434, 0, 0, 851970, 0, 0, 3, 0, 0, 65539, 0, 0, 131075, 0, 0, 196611, 0, 0, 262147, 0, 0, 327683, 0, 0, 393219, 0, 0, 458755, 0, 0, 524291, 0, 0, 589827, 0, 0, 655363, 0, 0, 720899, 0, 0, 786435, 0, 0, 851971, 0, 0, 4, 0, 0, 65540, 0, 0, 131076, 0, 0, 196612, 0, 0, 262148, 0, 0, 327684, 0, 0, 393220, 0, 0, 458756, 0, 0, 524292, 0, 0, 589828, 0, 0, 655364, 0, 0, 720900, 0, 0, 786436, 0, 0, 851972, 0, 0, 5, 0, 0, 65541, 0, 0, 131077, 0, 0, 196613, 0, 0, 262149, 0, 0, 327685, 0, 0, 393221, 0, 0, 458757, 0, 0, 524293, 0, 0, 589829, 0, 0, 655365, 0, 0, 720901, 0, 0, 786437, 0, 0, 851973, 0, 0, 6, 0, 0, 65542, 0, 0, 131078, 0, 0, 196614, 0, 0, 262150, 0, 0, 327686, 0, 0, 393222, 0, 0, 458758, 0, 0, 524294, 0, 0, 589830, 0, 0, 655366, 0, 0, 720902, 0, 0, 786438, 0, 0, 851974, 0, 0, 7, 0, 0, 65543, 0, 0, 131079, 0, 0, 196615, 0, 0, 262151, 0, 0, 327687, 0, 0, 393223, 0, 0, 458759, 0, 0, 524295, 0, 0, 589831, 0, 0, 655367, 0, 0, 720903, 0, 0, 786439, 0, 0, 851975, 0, 0, 8, 0, 0, 65544, 0, 0, 131080, 0, 0, 196616, 0, 0, 262152, 0, 0, 327688, 0, 0, 393224, 0, 0, 458760, 0, 0, 589832, 0, 0, 655368, 0, 0, 720904, 0, 0, 786440, 0, 0, 851976, 0, 0, 9, 0, 0, 65545, 0, 0, 131081, 0, 0, 196617, 0, 0, 262153, 0, 0, 327689, 0, 0, 393225, 0, 0, 458761, 0, 0, 655369, 0, 0, 720905, 0, 0, 786441, 0, 0, 851977, 0, 0, 10, 0, 0, 65546, 0, 0, 131082, 0, 0, 196618, 0, 0, 262154, 0, 0, 327690, 0, 0, 393226, 0, 0, 458762, 0, 0, 524298, 0, 0, 720906, 0, 0, 786442, 0, 0, 851978, 0, 0, 11, 0, 0, 65547, 0, 0, 131083, 0, 0, 196619, 0, 0, 262155, 0, 0, 327691, 0, 0, 393227, 0, 0, 458763, 0, 0, 524299, 0, 0, 589835, 0, 0, 720907, 0, 0, 786443, 0, 0, 851979, 0, 0, 12, 0, 0, 65548, 0, 0, 131084, 0, 0, 196620, 0, 0, 262156, 0, 0, 327692, 0, 0, 393228, 0, 0, 458764, 0, 0, 524300, 0, 0, 589836, 0, 0, 786444, 0, 0, 851980, 0, 0, 13, 0, 0, 65549, 0, 0, 131085, 0, 0, 196621, 0, 0, 262157, 0, 0, 327693, 0, 0, 393229, 0, 0, 458765, 0, 0, 524301, 0, 0, 589837, 0, 0, 655373, 0, 0, 786445, 0, 0, 14, 0, 0, 65550, 0, 0, 131086, 0, 0, 196622, 0, 0, 262158, 0, 0, 327694, 0, 0, 393230, 0, 0, 458766, 0, 0, 524302, 0, 0, 589838, 0, 0, 655374, 0, 0, 786446, 0, 0, 15, 0, 0, 65551, 0, 0, 131087, 0, 0, 196623, 0, 0, 262159, 0, 0, 327695, 0, 0, 393231, 0, 0, 458767, 0, 0, 524303, 0, 0, 589839, 0, 0, 655375, 0, 0, 786447, 0, 0, 16, 0, 0, 65552, 0, 0, 131088, 0, 0, 196624, 0, 0, 262160, 0, 0, 327696, 0, 0, 393232, 0, 0, 458768, 0, 0, 524304, 0, 0, 589840, 0, 0, 655376, 0, 0, 786448, 0, 0, 17, 0, 0, 65553, 0, 0, 131089, 0, 0, 196625, 0, 0, 262161, 0, 0, 327697, 0, 0, 393233, 0, 0, 458769, 0, 0, 524305, 0, 0, 589841, 0, 0, 655377, 0, 0, 720913, 0, 0, 18, 0, 0, 65554, 0, 0, 131090, 0, 0, 196626, 0, 0, 262162, 0, 0, 327698, 0, 0, 393234, 0, 0, 458770, 0, 0, 524306, 0, 0, 589842, 0, 0, 655378, 0, 0, 720914, 0, 0, 19, 0, 0, 65555, 0, 0, 131091, 0, 0, 196627, 0, 0, 262163, 0, 0, 327699, 0, 0, 393235, 0, 0, 458771, 0, 0, 524307, 0, 0, 589843, 0, 0, 655379, 0, 0, 720915, 0, 0, 20, 0, 0, 65556, 0, 0, 131092, 0, 0, 196628, 0, 0, 262164, 0, 0, 327700, 0, 0, 393236, 0, 0, 458772, 0, 0, 524308, 0, 0, 589844, 0, 0, 655380, 0, 0, 720916, 0, 0, 786452, 0, 0, 21, 0, 0, 65557, 0, 0, 131093, 0, 0, 196629, 0, 0, 262165, 0, 0, 327701, 0, 0, 393237, 0, 0, 458773, 0, 0, 524309, 0, 0, 589845, 0, 0, 655381, 0, 0, 720917, 0, 0, 786453, 0, 0, 917504, 0, 0, 917505, 0, 0, 917506, 0, 0, 917507, 0, 0, 917508, 0, 0, 917509, 0, 0, 917510, 0, 0, 917511, 0, 0, 917512, 0, 0, 917513, 0, 0, 917514, 0, 0, 917515, 0, 0, 917516, 0, 0, 851981, 0, 0, 851982, 0, 0, 851983, 0, 0, 851984, 0, 0, 851985, 0, 0, 851986, 0, 0, 851987, 0, 0, 851988, 0, 0, 851989, 0, 0, 917524, 0, 0, 917523, 0, 0, 917522, 0, 0, 917521, 0, 0, 917520, 0, 0, 917519, 0, 0, 917518, 0, 0, 917517, 0, 0, 0, 0, 0, 917525, 0, 0, 524296, 0, 0, 524297, 0, 0, 589833, 0, 0, 589834, 0, 0, 655370, 0, 0, 655371, 0, 0, 655372, 0, 0, 720908, 0, 0, 720909, 0, 0, 720910, 0, 0, 720911, 0, 0, 720912, 0, 0, 786449, 0, 0, 786450, 0, 0, 786451, 0, 0, 983040, 0, 0, 1048576, 0, 0, 1114112, 0, 0, 1179648, 0, 0, 983041, 0, 0, 1048577, 0, 0, 1114113, 0, 0, 1179649, 0, 0, 983042, 0, 0, 1048578, 0, 0, 1114114, 0, 0, 1179650, 0, 0, 983043, 0, 0, 1048579, 0, 0, 1114115, 0, 0, 1179651, 0, 0, 983044, 0, 0, 1048580, 0, 0, 1114116, 0, 0, 1179652, 0, 0, 983045, 0, 0, 1048581, 0, 0, 1114117, 0, 0, 1179653, 0, 0, 983046, 0, 0, 1048582, 0, 0, 1114118, 0, 0, 1179654, 0, 0, 983047, 0, 0, 1048583, 0, 0, 1114119, 0, 0, 1179655, 0, 0, 983048, 0, 0, 1048584, 0, 0, 1114120, 0, 0, 1179656, 0, 0, 983049, 0, 0, 1048585, 0, 0, 1114121, 0, 0, 1179657, 0, 0, 983050, 0, 0, 1048586, 0, 0, 1114122, 0, 0, 1179658, 0, 0, 983051, 0, 0, 1048587, 0, 0, 1114123, 0, 0, 1179659, 0, 0, 983052, 0, 0, 1048588, 0, 0, 1114124, 0, 0, 1179660, 0, 0, 983053, 0, 0, 1048589, 0, 0, 1114125, 0, 0, 1179661, 0, 0, 983054, 0, 0, 1048590, 0, 0, 1114126, 0, 0, 1179662, 0, 0, 983055, 0, 0, 1048591, 0, 0, 1114127, 0, 0, 1179663, 0, 0, 983056, 0, 0, 1048592, 0, 0, 1114128, 0, 0, 1179664, 0, 0, 983057, 0, 0, 1048593, 0, 0, 1114129, 0, 0, 1179665, 0, 0, 983058, 0, 0, 1048594, 0, 0, 1114130, 0, 0, 1179666, 0, 0, 983059, 0, 0, 1048595, 0, 0, 1114131, 0, 0, 1179667, 0, 0, 983060, 0, 0, 1048596, 0, 0, 1114132, 0, 0, 1179668, 0, 0, 983061, 0, 0, 1048597, 0, 0, 1114133, 0, 0, 1179669, 0, 0, 983062, 0, 0, 1048598, 0, 0, 1114134, 0, 0, 1179670, 0, 0, 983063, 0, 0, 1048599, 0, 0, 1114135, 0, 0, 1179671, 0, 0, 983064, 0, 0, 1048600, 0, 0, 1114136, 0, 0, 1179672, 0, 0, 983065, 0, 0, 1048601, 0, 0, 1114137, 0, 0, 1179673, 0, 0, 983066, 0, 0, 1048602, 0, 0, 1114138, 0, 0, 1179674, 0, 0, 983067, 0, 0, 1048603, 0, 0, 1114139, 0, 0, 1179675, 0, 0, 983068, 0, 0, 1048604, 0, 0, 1114140, 0, 0, 1179676, 0, 0, 983069, 0, 0, 1048605, 0, 0, 1114141, 0, 0, 1179677, 0, 0, 983070, 0, 0, 1048606, 0, 0, 1114142, 0, 0, 1179678, 0, 0, 983071, 0, 0, 1048607, 0, 0, 1114143, 0, 0, 1179679, 0, 0, 22, 0, 0, 65558, 0, 0, 131094, 0, 0, 196630, 0, 0, 262166, 0, 0, 327702, 0, 0, 393238, 0, 0, 458774, 0, 0, 524310, 0, 0, 589846, 0, 0, 655382, 0, 0, 720918, 0, 0, 786454, 0, 0, 851990, 0, 0, 917526, 0, 0, 23, 0, 0, 65559, 0, 0, 131095, 0, 0, 196631, 0, 0, 262167, 0, 0, 327703, 0, 0, 393239, 0, 0, 458775, 0, 0, 524311, 0, 0, 589847, 0, 0, 655383, 0, 0, 720919, 0, 0, 786455, 0, 0, 851991, 0, 0, 917527, 0, 0, 24, 0, 0, 65560, 0, 0, 131096, 0, 0, 196632, 0, 0, 262168, 0, 0, 327704, 0, 0, 393240, 0, 0, 458776, 0, 0, 524312, 0, 0, 589848, 0, 0, 655384, 0, 0, 720920, 0, 0, 786456, 0, 0, 851992, 0, 0, 917528, 0, 0, 25, 0, 0, 65561, 0, 0, 131097, 0, 0, 196633, 0, 0, 262169, 0, 0, 327705, 0, 0, 393241, 0, 0, 458777, 0, 0, 524313, 0, 0, 589849, 0, 0, 655385, 0, 0, 720921, 0, 0, 786457, 0, 0, 851993, 0, 0, 917529, 0, 0, 26, 0, 0, 65562, 0, 0, 131098, 0, 0, 196634, 0, 0, 262170, 0, 0, 327706, 0, 0, 393242, 0, 0, 458778, 0, 0, 524314, 0, 0, 589850, 0, 0, 655386, 0, 0, 720922, 0, 0, 786458, 0, 0, 851994, 0, 0, 917530, 0, 0, 27, 0, 0, 65563, 0, 0, 131099, 0, 0, 196635, 0, 0, 262171, 0, 0, 327707, 0, 0, 393243, 0, 0, 458779, 0, 0, 524315, 0, 0, 589851, 0, 0, 655387, 0, 0, 720923, 0, 0, 786459, 0, 0, 851995, 0, 0, 917531, 0, 0, 28, 0, 0, 65564, 0, 0, 131100, 0, 0, 196636, 0, 0, 262172, 0, 0, 327708, 0, 0, 393244, 0, 0, 458780, 0, 0, 524316, 0, 0, 589852, 0, 0, 655388, 0, 0, 720924, 0, 0, 786460, 0, 0, 851996, 0, 0, 917532, 0, 0, 29, 0, 0, 65565, 0, 0, 131101, 0, 0, 196637, 0, 0, 262173, 0, 0, 327709, 0, 0, 393245, 0, 0, 458781, 0, 0, 524317, 0, 0, 589853, 0, 0, 655389, 0, 0, 720925, 0, 0, 786461, 0, 0, 851997, 0, 0, 917533, 0, 0, 30, 0, 0, 65566, 0, 0, 131102, 0, 0, 196638, 0, 0, 262174, 0, 0, 327710, 0, 0, 393246, 0, 0, 458782, 0, 0, 524318, 0, 0, 589854, 0, 0, 655390, 0, 0, 720926, 0, 0, 786462, 0, 0, 851998, 0, 0, 917534, 0, 0, 31, 0, 0, 65567, 0, 0, 131103, 0, 0, 196639, 0, 0, 262175, 0, 0, 327711, 0, 0, 393247, 0, 0, 458783, 0, 0, 524319, 0, 0, 589855, 0, 0, 655391, 0, 0, 720927, 0, 0, 786463, 0, 0, 851999, 0, 0, 917535, 0, 0, 32, 0, 0, 65568, 0, 0, 131104, 0, 0, 196640, 0, 0, 262176, 0, 0, 327712, 0, 0, 393248, 0, 0, 458784, 0, 0, 524320, 0, 0, 33, 0, 0, 65569, 0, 0, 131105, 0, 0, 196641, 0, 0, 262177, 0, 0, 327713, 0, 0, 393249, 0, 0, 458785, 0, 0, 524321, 0, 0, 589856, 0, 0, 655392, 0, 0, 720928, 0, 0, 786464, 0, 0, 852000, 0, 0, 917536, 0, 0, 983072, 0, 0, 1048608, 0, 0, 1114144, 0, 0, 1179680, 0, 0, 589857, 0, 0, 655393, 0, 0, 720929, 0, 0, 786465, 0, 0, 852001, 0, 0, 917537, 0, 0, 983073, 0, 0, 1048609, 0, 0, 1114145, 0, 0, 1179681, 0, 0, 34, 0, 0, 65570, 0, 0, 131106, 0, 0, 196642, 0, 0, 262178, 0, 0, 327714, 0, 0, 393250, 0, 0, 458786, 0, 0, 524322, 0, 0, 589858, 0, 0, 655394, 0, 0, 720930, 0, 0, 786466, 0, 0, 852002, 0, 0, 917538, 0, 0, 983074, 0, 0, 1048610, 0, 0, 1114146, 0, 0, 1179682, 0, 0, 1245183, 0, 0, 1179647, 0, 0, 1114111, 0, 0, 1048575, 0, 0, 983039, 0, 0, 917503, 0, 0, 851967, 0, 0, 786431, 0, 0, 720895, 0, 0, 655359, 0, 0, 589823, 0, 0, 524287, 0, 0, 458751, 0, 0, 393215, 0, 0, 327679, 0, 0, 262143, 0, 0, 196607, 0, 0, 131071, 0, 0, 65535, 0, 0, -1, 0, 0, -65536, 0, 0, -65535, 0, 0, -65534, 0, 0, -65533, 0, 0, -65532, 0, 0, -65531, 0, 0, -65530, 0, 0, -65529, 0, 0, -65528, 0, 0, -65527, 0, 0, -65526, 0, 0, -65525, 0, 0, -65524, 0, 0, -65523, 0, 0, -65522, 0, 0, -65521, 0, 0, -65520, 0, 0, -65519, 0, 0, -65518, 0, 0, -65517, 0, 0, -65516, 0, 0, -65515, 0, 0, -65514, 0, 0, -65513, 0, 0, -65512, 0, 0, -65511, 0, 0, -65510, 0, 0, -65509, 0, 0, -65508, 0, 0, -65507, 0, 0, -65506, 0, 0, -65505, 0, 0, -65504, 0, 0, -65503, 0, 0, -65502, 0, 0) +layer_1/name = "Castle" +layer_1/tile_data = PackedInt32Array(589840, 131072, 6, 655376, 131072, 7, 720912, 131072, 8, 786448, 131072, 9, 851984, 131072, 10, 589841, 196608, 6, 655377, 196608, 7, 720913, 196608, 8, 851985, 196608, 4, 589842, 262144, 6, 655378, 262144, 7, 720914, 262144, 8, 786450, 262144, 9, 851986, 65536, 0, 589843, 327680, 6, 655379, 327680, 7, 720915, 327680, 8, 786451, 262144, 4, 851987, 196608, 4, 589844, 393216, 6, 655380, 196608, 4, 720916, 262144, 8, 786452, 196608, 4, 851988, 262144, 4, 524310, 458752, 1, 589846, 458752, 2, 655382, 196608, 4, 720918, 720896, 4, 786454, 196608, 4, 851990, 458752, 6, 458775, 524288, 0, 524311, 524288, 1, 589847, 524288, 2, 655383, 524288, 3, 720919, 720896, 4, 786455, 720896, 4, 851991, 524288, 6, 458776, 589824, 0, 524312, 589824, 1, 589848, 589824, 2, 655384, 589824, 3, 720920, 589824, 4, 786456, 589824, 5, 851992, 589824, 6, 458777, 655360, 0, 524313, 655360, 1, 589849, 655360, 2, 655385, 655360, 3, 720921, 655360, 4, 786457, 655360, 5, 851993, 655360, 6, 458778, 720896, 0, 524314, 720896, 1, 589850, 720896, 2, 655386, 720896, 3, 720922, 720896, 4, 786458, 720896, 5, 851994, 720896, 6, 458779, 786432, 0, 524315, 786432, 1, 589851, 786432, 2, 655387, 786432, 3, 720923, 786432, 4, 786459, 786432, 5, 851995, 786432, 6, 458780, 851968, 0, 524316, 851968, 1, 589852, 851968, 2, 655388, 851968, 3, 720924, 851968, 4, 786460, 851968, 5, 851996, 851968, 6, 524317, 917504, 1, 589853, 917504, 2, 655389, 917504, 3, 720925, 917504, 4, 786461, 917504, 5, 851997, 917504, 6, 917527, 524288, 7, 917528, 589824, 7, 917529, 655360, 7, 917530, 720896, 7, 917531, 786432, 7, 917532, 851968, 7, 786453, 196608, 4, 655381, 196608, 4, 786449, 262144, 8, 720917, 262144, 0, 786436, 262144, 8, 851973, 262144, 8, 917509, 262144, 8, 786437, 262144, 8, 851972, 262144, 8, 917508, 262144, 8, 983044, 262144, 0, 983045, 655360, 4, 1048581, 655360, 4, 1048580, 655360, 4, 1114116, 655360, 4, 1114117, 655360, 4, 720901, 65536, 0, 327692, 262144, 4, 262156, 262144, 4, 196620, 262144, 4, 131084, 262144, 4, 131085, 262144, 4, 131086, 262144, 4, 131087, 262144, 4, 131088, 262144, 4, 196624, 262144, 4, 262160, 262144, 4, 327696, 262144, 4, 393232, 262144, 4, 393228, 262144, 4, 131105, 196608, 4, 196641, 196608, 4, 131102, 196608, 4, 196638, 196608, 4, 262174, 196608, 4, 262177, 196608, 4, 327710, 196608, 4, 327711, 196608, 4, 327712, 196608, 4, 327713, 196608, 4, 131104, 196608, 4, 131103, 196608, 4, 851978, 65536, 0, 917514, 262144, 0, 983050, 655360, 3) + +[node name="MarkerFrom" parent="." instance=ExtResource("3_iwmvr")] + +[node name="MarkerTo" parent="." instance=ExtResource("3_iwmvr")] + +[node name="PathLine" type="Line2D" parent="."] +width = 2.0 + +[node name="Units" type="Node2D" parent="."] + +[node name="Unit1" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(256, 192) + +[node name="Unit2" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(224, 224) + +[node name="Unit3" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(576, 512) + +[node name="Unit4" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(576, 544) + +[node name="Unit10" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(640, 96) + +[node name="Unit11" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(672, 96) + +[node name="Unit12" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(608, 96) + +[node name="Unit13" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(640, 64) + +[node name="Unit14" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(32, 288) + +[node name="Unit15" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(64, 288) + +[node name="Unit16" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(0, 288) + +[node name="Unit17" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(32, 256) + +[node name="Unit" parent="Units" instance=ExtResource("4_rc8be")] +position = Vector2(32, 320) + +[node name="CheckBox" type="CheckBox" parent="."] +offset_left = 25.0 +offset_top = 3.0 +offset_right = 164.0 +offset_bottom = 34.0 +focus_mode = 0 +text = "Return closest" + +[node name="Label" type="Label" parent="."] +offset_left = 27.0 +offset_top = 39.0 +offset_right = 117.0 +offset_bottom = 88.0 +text = "Red - Start (RMB) +Green - End (LMB) +" + +[connection signal="pressed" from="CheckBox" to="." method="_on_check_box_pressed"] diff --git a/demo/markers/marker.png b/demo/markers/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..bac1cf707a4aea88525060d66d94f6df0bfc2568 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}TRmMILn2z= zPWR?(P~dS{sjfJu@pt^%JQc%<6U9!cdrfcICm9u>5H5J;%+5k3C0*}14K9ii8Otm* z)q5Vy+Ns1`zF^y)OLrc5W&K)}Xv?N^RP_wAYGmi?h?>@^8@ldC%e*`w#?u?P`ut1Z z^#@9rr+6h+zESzuE5;&k`Qyfx%H`n+?-`%p`6r+&^#7mqvKdv1FLRj+6S;MK_iVhj zds?()X4MRZ0M3kmCfWz%4~F#z_s*R4w}`3a@1+@KN(a{iJ;31U>gTe~DWM4fsF`FF literal 0 HcmV?d00001 diff --git a/demo/markers/marker.png.import b/demo/markers/marker.png.import new file mode 100644 index 0000000..40786a9 --- /dev/null +++ b/demo/markers/marker.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ydnd8d7pbpct" +path="res://.godot/imported/marker.png-302bb6fffff2bb75ddac5872ea46d55a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://markers/marker.png" +dest_files=["res://.godot/imported/marker.png-302bb6fffff2bb75ddac5872ea46d55a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/demo/markers/marker.tscn b/demo/markers/marker.tscn new file mode 100644 index 0000000..308fa54 --- /dev/null +++ b/demo/markers/marker.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://cnrhrho63n4n2"] + +[ext_resource type="Texture2D" uid="uid://ydnd8d7pbpct" path="res://markers/marker.png" id="1_mefdn"] + +[node name="Marker" type="Node2D"] + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_mefdn") +offset = Vector2(16, 16) diff --git a/demo/markers/unit.png b/demo/markers/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ab9713841c17a34579e050467068b3b72eb8d3 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}VV*9IArY-_ zuW#gJFywI#e7(cbDec6+)tXb)Ejl>9$L{>>+`A!XchhH;{_xPfdp$0lJUq9?h7qWf xfuX=Wq3@~XwdGd*>I>GnzLEe7E^*uczt4Qi8*#bXwHB2iAx~F7mvv4FO#pzBH97zQ literal 0 HcmV?d00001 diff --git a/demo/markers/unit.png.import b/demo/markers/unit.png.import new file mode 100644 index 0000000..2b3025f --- /dev/null +++ b/demo/markers/unit.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6j8es5qgtv05" +path="res://.godot/imported/unit.png-1fe68df64273b07deeeaa2e1953638b5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://markers/unit.png" +dest_files=["res://.godot/imported/unit.png-1fe68df64273b07deeeaa2e1953638b5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/demo/markers/unit.tscn b/demo/markers/unit.tscn new file mode 100644 index 0000000..454a496 --- /dev/null +++ b/demo/markers/unit.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://cj34xrleexqf6"] + +[ext_resource type="Texture2D" uid="uid://b6j8es5qgtv05" path="res://markers/unit.png" id="1_qutcq"] + +[node name="Unit" type="Node2D" groups=["unit"]] + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(16, 16) +texture = ExtResource("1_qutcq") diff --git a/demo/project.godot b/demo/project.godot new file mode 100644 index 0000000..f266208 --- /dev/null +++ b/demo/project.godot @@ -0,0 +1,20 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="MultilevelAStarTest" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.2", "Forward Plus") +config/icon="res://icon.svg" + +[display] + +window/stretch/mode="canvas_items" diff --git a/demo/tiles/socerb.png b/demo/tiles/socerb.png new file mode 100644 index 0000000000000000000000000000000000000000..437308377d227646e8f18786039b8af2653b2803 GIT binary patch literal 36800 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 +#include +#include + +#include +#include +#include + +#define TERRAIN(x, y) (_terrain[(x) + (y) * _width]) +#define UNITS(x, y) (_units[(x) + (y) * _width]) +#define NODES(x, y) (_nodes[(x) + (y) * _width]) + +using namespace godot; + +Node::Node() { } + +Node::Node(int x, int y) +{ + this->x = x; + this->y = y; + this->state = UNUSED; +} + +void Node::init(Node* parent, int distanceFromStart, const Vector2i &end) +{ + this->state = OPEN; + this->parent = parent; + this->distanceFromStart = distanceFromStart; + + int dx = abs(x - end.x); + int dy = abs(y - end.y); + this->distanceToEnd = (dx + dy) * 10; + + if ((dx == 0 || dx == 1) && (dy == 0 || dy == 1)) + { + this->distanceToEndForClosest = 10; + } + else + { + this->distanceToEndForClosest = this->distanceToEnd; + } +} + +int Node::total_cost() const +{ + return distanceFromStart + distanceToEnd; +} + +void MultilevelAStarEx::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("init", "region"), &MultilevelAStarEx::init); + ClassDB::bind_method(D_METHOD("get_region"), &MultilevelAStarEx::get_region); + ClassDB::bind_method(D_METHOD("set_terrain", "cell", "type"), &MultilevelAStarEx::set_terrain); + ClassDB::bind_method(D_METHOD("get_terrain", "cell"), &MultilevelAStarEx::get_terrain); + ClassDB::bind_method(D_METHOD("set_unit", "cell", "blocked"), &MultilevelAStarEx::set_unit); + ClassDB::bind_method(D_METHOD("get_unit", "cell"), &MultilevelAStarEx::get_unit); + ClassDB::bind_method(D_METHOD("find_path", "from", "to", "return_closest"), &MultilevelAStarEx::find_path); + + BIND_ENUM_CONSTANT(STAIRS); + BIND_ENUM_CONSTANT(BLOCKED); + BIND_ENUM_CONSTANT(GROUND); +} + +MultilevelAStarEx::MultilevelAStarEx() +{ + //UtilityFunctions::print("Constructor."); + + _init = false; +} + +MultilevelAStarEx::~MultilevelAStarEx() +{ + //UtilityFunctions::print("Destructor."); +} + +void MultilevelAStarEx::init(const Rect2i ®ion) +{ + DEV_ASSERT(!_init); + + DEV_ASSERT(region.get_area() > 0); + + _region = region; + _width = region.get_size().width; + _height = region.get_size().height; + _trans = region.get_position(); + + _terrain.resize(_width * _height, BLOCKED); + _units.resize(_width * _height, false); + _nodes.resize(_width * _height); + + std::vector::iterator iter = _nodes.begin(); + for (int y = 0; y < _height; y++) + { + for (int x = 0; x < _width; x++) + { + *iter++ = Node(x, y); + } + } + + _init = true; +} + +Rect2i MultilevelAStarEx::get_region() const +{ + DEV_ASSERT(_init); + + return _region; +} + +void MultilevelAStarEx::set_terrain(const Vector2i &cell, TerrainType type) +{ + DEV_ASSERT(_init); + DEV_ASSERT(_region.has_point(cell)); + + Vector2i cell2 = cell - _trans; + TERRAIN(cell2.x, cell2.y) = type; +} + +MultilevelAStarEx::TerrainType MultilevelAStarEx::get_terrain(const Vector2i &cell) const +{ + DEV_ASSERT(_init); + DEV_ASSERT(_region.has_point(cell)); + + Vector2i cell2 = cell - _trans; + return TERRAIN(cell2.x, cell2.y); +} + +void MultilevelAStarEx::set_unit(const Vector2i &cell, bool blocked) +{ + DEV_ASSERT(_init); + DEV_ASSERT(_region.has_point(cell)); + + Vector2i cell2 = cell - _trans; + UNITS(cell2.x, cell2.y) = blocked; +} + +bool MultilevelAStarEx::get_unit(const Vector2i &cell) const +{ + DEV_ASSERT(_init); + DEV_ASSERT(_region.has_point(cell)); + + Vector2i cell2 = cell - _trans; + return UNITS(cell2.x, cell2.y); +} + +bool MultilevelAStarEx::can_move(const Node* current, int x, int y) const +{ + if (UNITS(x, y)) return false; + + TerrainType tc = TERRAIN(current->x, current->y); + TerrainType td = TERRAIN(x, y); + + if (td == BLOCKED) + { + return false; + } + else if (td == tc) + { + return true; + } + else if (td < 0 && tc > 0) // td is stairs + { + if (-td == tc || -td == tc - 1) return true; + } + else if (td > 0 && tc < 0) // tc is stairs + { + if (-tc == td || -tc == td - 1) return true; + } + else if (td < 0 && tc < 0) // both are stairs + { + return abs(td - tc) < 2; + } + + return false; +} + +TypedArray MultilevelAStarEx::generate_path(const Node* current) const +{ + TypedArray arr; + + while (current->parent != nullptr) + { + arr.insert(0, Vector2i(current->x, current->y) + _trans); + current = current->parent; + } + + return arr; +} + +Variant MultilevelAStarEx::find_path(const Vector2i &from, const Vector2i &to, const bool return_closest) +{ + DEV_ASSERT(_init); + + DEV_ASSERT(_region.has_point(from)); + DEV_ASSERT(_region.has_point(to)); + + Vector2i from2 = from - _trans; + Vector2i to2 = to - _trans; + + Variant result; + + std::vector open; + std::vector closed; + + open.reserve(_width * _height); + closed.reserve(_width * _height); + + Node* closest = &NODES(from2.x, from2.y); + closest->init(nullptr, 0, to2); + open.push_back(closest); + + auto process = [this, &open, &closed, &to2, &closest](Node* current, int x, int y, int distance) { + Node* node = &NODES(x, y); + if (node->state == Node::UNUSED) + { + node->init(current, current->distanceFromStart + distance, to2); + open.push_back(node); + } + else if (node->state == Node::OPEN) + { + if (current->distanceFromStart < node->parent->distanceFromStart) + { + node->parent = current; + node->distanceFromStart = current->distanceFromStart + distance; + } + } + + // find the closest cell + if (node->distanceToEndForClosest < closest->distanceToEndForClosest) + { + closest = node; + } + else if (node->distanceToEndForClosest == closest->distanceToEndForClosest) + { + if (node->distanceFromStart < closest->distanceFromStart) + { + closest = node; + } + } + }; + + while (open.size() > 0) + { + // find closest to destination + Node* current = open[0]; + for (Node* n : open) + { + if (n->total_cost() < current->total_cost()) + { + current = n; + } + } + + if (current->distanceToEnd == 0) + { + // found the path + result = Variant(generate_path(current)); + goto cleanup; + } + + // close it + current->state = Node::CLOSED; + open.erase(std::remove(open.begin(), open.end(), current), open.end()); + closed.push_back(current); + + // expand it + if (current->x - 1 >= 0) // left + { + if (can_move(current, current->x - 1, current->y)) + { + process(current, current->x - 1, current->y, 10); + } + } + if (current->x + 1 < _width) // right + { + if (can_move(current, current->x + 1, current->y)) + { + process(current, current->x + 1, current->y, 10); + } + } + if (current->y - 1 >= 0) // up + { + if (can_move(current, current->x, current->y - 1)) + { + process(current, current->x, current->y - 1, 10); + } + } + if (current->y + 1 < _height) // down + { + if (can_move(current, current->x, current->y + 1)) + { + process(current, current->x, current->y + 1, 10); + } + } + if ((current->x - 1 >= 0) && (current->y - 1 >= 0)) // top left + { + if (can_move(current, current->x - 1, current->y - 1)) + { + process(current, current->x - 1, current->y - 1, 14); + } + } + if ((current->x + 1 < _width) && (current->y - 1 >= 0)) // top right + { + if (can_move(current, current->x + 1, current->y - 1)) + { + process(current, current->x + 1, current->y - 1, 14); + } + } + if ((current->x - 1 >= 0) && (current->y + 1 < _height)) // bottom left + { + if (can_move(current, current->x - 1, current->y + 1)) + { + process(current, current->x - 1, current->y + 1, 14); + } + } + if ((current->x + 1 < _width) && (current->y + 1 < _height)) // bottom right + { + if (can_move(current, current->x + 1, current->y + 1)) + { + process(current, current->x + 1, current->y + 1, 14); + } + } + } + + // this is skipped if a path was found + if (return_closest) + { + // return path to closest + result = Variant(generate_path(closest)); + } + +cleanup: + // release the nodes + for (Node* node : open) + { + node->state = Node::UNUSED; + } + for (Node* node : closed) + { + node->state = Node::UNUSED; + } + + return result; +} diff --git a/src/MultilevelAStarEx.h b/src/MultilevelAStarEx.h new file mode 100644 index 0000000..e967744 --- /dev/null +++ b/src/MultilevelAStarEx.h @@ -0,0 +1,85 @@ +#ifndef MultilevelAStarEx_H +#define MultilevelAStarEx_H + +#include +#include +#include +#include + +#include + +namespace godot { + +class Node { + friend class MultilevelAStarEx; + +public: + enum NodeState + { + UNUSED, OPEN, CLOSED + }; + +private: + NodeState state; + int x, y; + + Node* parent; + int distanceFromStart; + int distanceToEnd; + + int distanceToEndForClosest; + + Node(int x, int y); + void init(Node* parent, int distanceFromStart, const Vector2i &end); + int total_cost() const; + +public: + Node(); +}; + +class MultilevelAStarEx : public RefCounted +{ + GDCLASS(MultilevelAStarEx, RefCounted) + +public: + enum TerrainType + { + STAIRS = -1, + BLOCKED = 0, + GROUND = 1, + }; + +private: + bool _init; + std::vector _terrain; + std::vector _units; + std::vector _nodes; + + Rect2i _region; + Vector2i _trans; + int _width, _height; + + bool can_move(const Node* current, int x, int y) const; + TypedArray generate_path(const Node* current) const; + +protected: + static void _bind_methods(); + +public: + MultilevelAStarEx(); + ~MultilevelAStarEx(); + + void init(const Rect2i ®ion); + Rect2i get_region() const; + void set_terrain(const Vector2i &cell, TerrainType type); + TerrainType get_terrain(const Vector2i &cell) const; + void set_unit(const Vector2i &cell, bool blocked); + bool get_unit(const Vector2i &cell) const; + Variant find_path(const Vector2i &from, const Vector2i &to, const bool return_closest); +}; + +} + +VARIANT_ENUM_CAST(MultilevelAStarEx::TerrainType); + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..12e2b50 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,36 @@ +#include "MultilevelAStarEx.h" + +#include +#include +#include + +using namespace godot; + +void initialize_module(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + ClassDB::register_class(); +} + +void uninitialize_module(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} + +extern "C" { +GDExtensionBool GDE_EXPORT my_astar_ext_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) +{ + godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); + + init_obj.register_initializer(initialize_module); + init_obj.register_terminator(uninitialize_module); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + + return init_obj.init(); +} +} \ No newline at end of file diff --git a/term.cmd b/term.cmd new file mode 100644 index 0000000..ccf03dc --- /dev/null +++ b/term.cmd @@ -0,0 +1,2 @@ +@set PATH=%PATH%;D:\Programs\scons +@cmd \ No newline at end of file