From e380a18ee3f0950da390c0e64529fd418589f6d7 Mon Sep 17 00:00:00 2001 From: Jonas Mucke Date: Mon, 20 Apr 2020 20:52:07 +0200 Subject: [PATCH] finished player ai --- src/Maps/Grid.gd | 218 ++++++++++++++++++++++++++---------- src/Overlap/AI/AI_Hero.gd | 229 +++++++------------------------------- src/Player/Player.gd | 13 +-- src/Player/Player.tscn | 2 +- src/World.tscn | 2 +- 5 files changed, 203 insertions(+), 261 deletions(-) diff --git a/src/Maps/Grid.gd b/src/Maps/Grid.gd index 6253da2..c5a2514 100644 --- a/src/Maps/Grid.gd +++ b/src/Maps/Grid.gd @@ -1,32 +1,104 @@ extends Node const Kind = preload("res://Overlap/Kind.gd") # Relative path +onready var aStar_node = AStar.new() + +var path_start_position : Vector2= Vector2() setget _set_path_start_position +var path_end_position : Vector2 = Vector2() setget _set_path_end_position + +var obstacles = [] +var _point_path = [] +var _half_cell_size = Vector2() var object_grid : Array = [] var prio_grid : Array = [] -var used_grid : Array = [] var time_passed := 0.0 var offset -export(float, 0, 42.0) var refresh_rate = 0.0 +export(float, 0, 42.0) var refresh_rate = 0.4 + +func _point_coors(point : Vector2): + return 14*point.y+point.x + +func _ready(): + var walls = get_tree().current_scene.get_node("FloorTileMap") + offset = walls.global_position -func _draw_object_grid(): + for x in range(14): + object_grid.push_back([]) + prio_grid.push_back([]) + for y in range(7): + object_grid[x].push_back([Kind.FIELD]) + prio_grid[x].push_back([Kind.TERMINAL_SYMBOL]) + + + for tile in walls.get_used_cells(): + if(is_in_coord(tile)): + object_grid[tile.x][tile.y][0] = Kind.WALL + obstacles.push_back(Vector2(tile.x, tile.y)) + + var walkableCells = [] for y in range(7): - var stri = "" for x in range(14): - stri += str(object_grid[x][y]) + " " - print(stri) - print() - - -func _draw_prio_grid(): - for y in range(7): - var stri = "" - for x in range(14): - stri += str(prio_grid[x][y]) + " " - print(stri) - print() - + if object_grid[x][y][0] == Kind.FIELD: + walkableCells.push_back(Vector2(x,y)) + var Index = _point_coors(Vector2(x,y)) + aStar_node.add_point(Index, Vector3(x,y,0.0)) + + #add points straight + for point in walkableCells: + var point_index = _point_coors(point) + + var points_relative_str = PoolVector2Array([ + Vector2(point.x + 1, point.y), + Vector2(point.x - 1, point.y), + Vector2(point.x , point.y + 1), + Vector2(point.x , point.y - 1) + ]) + for point_rel in points_relative_str: + var point_relative_index = _point_coors(point_rel) + + if point_rel == point or not is_in_coord(point_rel): + continue + if not aStar_node.has_point(point_relative_index): + continue + aStar_node.connect_points(point_index, point_relative_index, true) + + #diagonal + for point in walkableCells: + var point_index = _point_coors(point) + + var points_relative_dia = PoolVector2Array([ + Vector2(point.x + 1, point.y + 1), Vector2(point.x, point.y + 1), Vector2(point.x + 1, point.y), + Vector2(point.x - 1, point.y + 1), Vector2(point.x, point.y + 1), Vector2(point.x - 1, point.y), + Vector2(point.x + 1, point.y - 1), Vector2(point.x, point.y - 1), Vector2(point.x + 1, point.y), + Vector2(point.x - 1, point.y - 1), Vector2(point.x, point.y - 1), Vector2(point.x - 1, point.y) + ]) + + for i in range(points_relative_dia.size()/3): + var p_targ = points_relative_dia[i*3] + var p_ch1 = points_relative_dia[i*3+1] + var p_ch2 = points_relative_dia[i*3+2] + + var p_targ_c = _point_coors(p_targ) + var p_ch1_c = _point_coors(p_targ) + var p_ch2_c = _point_coors(p_targ) + + if p_targ == point or not is_in_coord(p_targ) and not aStar_node.has_point(p_targ_c): + continue + if p_ch1 == point or not is_in_coord(p_ch1) and not aStar_node.has_point(p_ch1_c): + continue + if p_ch2 == point or not is_in_coord(p_ch2) and not aStar_node.has_point(p_ch2_c): + continue + + aStar_node.connect_points(point_index, p_targ_c, true) + + +func recalculate_path(): + _point_path = [] + var start_index = _point_coors(path_start_position) + var end_index = _point_coors(path_end_position) + _point_path = aStar_node.get_point_path(start_index, end_index) func _reset_grids(): for x in range(14): @@ -37,33 +109,6 @@ func _reset_grids(): prio_grid[x][y].pop_back() -func _ready(): - - var walls = get_tree().current_scene.get_node("FloorTileMap") - offset = walls.global_position - #todo put in grid_lul - for x in range(14): - object_grid.push_back([]) - prio_grid.push_back([]) - used_grid.push_back([]) - for y in range(7): - object_grid[x].push_back([Kind.FIELD]) - prio_grid[x].push_back([Kind.TERMINAL_SYMBOL]) - used_grid[x].push_back(false) - - for tile in walls.get_used_cells(): - if(_is_in_grid(tile)): - object_grid[tile.x][tile.y][0] = Kind.WALL - - _update_grid() - - -func reset_history(): - for x in range(14): - for y in range(7): - used_grid[x][y] = false - - func countTargets(table): for i in range(table.size()): table[i]=0 @@ -83,19 +128,6 @@ func _pixel_to_grid_coords(pixel : Vector2) -> Vector2: new_coords.y = floor((pixel.y-offset.y) / 32.0) return new_coords - -func _is_in_grid(grid_coords : Vector2) -> bool: - if(grid_coords.x < 0): - return false - if(grid_coords.x > 13): - return false - if(grid_coords.y < 0): - return false - if(grid_coords.y > 6): - return false - return true - - func get_nearest(position, kind): var list = [] for x in range(14): @@ -115,6 +147,23 @@ func get_nearest(position, kind): mini = i return list[mini] +func get_fields_around(point): + var points_relative_str = PoolVector2Array([ + Vector2(point.x + 1, point.y + 1), + Vector2(point.x - 1, point.y + 1), + Vector2(point.x + 1, point.y - 1), + Vector2(point.x - 1, point.y - 1) + ]) + var point_list = [] + for point_rel in points_relative_str: + var point_relative_index = _point_coors(point_rel) + if point_rel == point or not is_in_coord(point_rel): + continue + if not aStar_node.has_point(point_relative_index): + continue + point_list.push_back(point_rel) + return point_list + func _update_grid(): _reset_grids() @@ -122,14 +171,65 @@ func _update_grid(): for node in world.get_children(): var node_kind = node.get_node("Kind") var grid_corrds = _pixel_to_grid_coords(node.global_position) - if (_is_in_grid(grid_corrds)): + if (is_in_coord(grid_corrds)): if(node_kind.general != Kind.FIELD and node_kind.general != Kind.WALL): object_grid[grid_corrds.x][grid_corrds.y].push_back(node_kind.general) prio_grid[grid_corrds.x][grid_corrds.y].push_back(node_kind.kind) - + + for y in range(7): + for x in range(14): + var index = _point_coors(Vector2(x,y)) + var scale = 1.0 + for val in object_grid[x][y]: + match val: + Kind.DAMAGE: + scale += 16 + Kind.HEALING: + scale -= 8 + Kind.SLOW: + scale += 8 + var neighboor_list = get_fields_around(Vector2(x,y)) + for neighboor in neighboor_list: + for val in object_grid[neighboor.x][neighboor.y]: + match val: + Kind.DAMAGE: + scale += 16 + Kind.HEALING: + scale -= 8 + Kind.SLOW: + scale += 8 + if(scale<0): + scale = 0 + aStar_node.set_point_weight_scale(index, scale) func _physics_process(delta): if(time_passed > refresh_rate): time_passed -= refresh_rate _update_grid() time_passed += delta + + +func is_in_coord(point): + if point[0]<0 || point[0]>13: + return false + if point[1]<0 || point[1]>6: + return false + return true + +func _set_path_start_position(value : Vector2): + if value in obstacles: + return + if not is_in_coord(value): + return + + path_start_position = value + + + +func _set_path_end_position(value : Vector2): + if value in obstacles: + return + if not is_in_coord(value): + return + + path_end_position = value diff --git a/src/Overlap/AI/AI_Hero.gd b/src/Overlap/AI/AI_Hero.gd index c88b2d4..51a4c86 100644 --- a/src/Overlap/AI/AI_Hero.gd +++ b/src/Overlap/AI/AI_Hero.gd @@ -6,6 +6,7 @@ const PrioQueue = preload("prio_queue.gd") # Relative path const Grid = preload("res://Maps/Grid.gd") var grid +var lock = Mutex.new() enum{ STEP, @@ -73,7 +74,7 @@ func calcPrioTable(): i += 1 return table -#14+7 0.999 + #updates heart and bonfire prio func adjustPrio(currentHealth, maxHealth): var prioVal = 20.0 - (float(currentHealth)/float(maxHealth))*20.0 @@ -94,157 +95,27 @@ func calcEnemyKind(): return i #returns a move -func getMoveDescription(myPosition : Vector2, targetPositions): - return AStar(myPosition, targetPositions) - - -func getCost(field): - var cost = 0 - for i in grid.object_grid[field.x][field.y]: - match i: - Grid.Kind.DAMAGE: - cost += 15 - Grid.Kind.SLOW: - cost += 4 - return cost +func getMoveDescription(myPosition : Vector2, targetPositions : Vector2): + grid.path_start_position = myPosition + grid.path_end_position = targetPositions + grid.recalculate_path() + if(grid._point_path.size()<=1): + return [NOTHING, [0,0]] + var to = grid._point_path[1] + var from = grid._point_path[0] + var p1 = pow(to[0]-from[0],2) + var p2 = pow(to[1]-from[1],2) -#return an heurestic of distance -# curr - current position -# targ - a target position -func h_fn(curr, target): - return 0 - var h = min(target[0]-curr[0],target[1]-curr[1]) - return h - -# currCost - currentCost -# target - position of the field to move to -func g_fn(currCost, target): - return currCost + getCost(target) + var norm = sqrt(p1+p2) + var move = STEP + if(norm > 1.0 && p1 != p2): + move = ROLL + return [move, grid._point_path[1]] -# Returns the list of adjacent nodes -func adjacent(currentPosition, can_roll = true): - var adj := [] - #adj.append([STEP, Vector2(0,0)]) - var p = currentPosition - var pot_adj_step = [[p[0]-1, p[1]-1], [p[0]-0, p[1]-1], [p[0]+1, p[1]-1], - [p[0]-1, p[1]-0], [p[0]+1, p[1]+0], - [p[0]-1, p[1]+1], [p[0]+0, p[1]+1], [p[0]+1, p[1]+1]] - - var pot_adj_roll = [[p[0]-0, p[1]-2], - [p[0]-2, p[1]-0],[p[0]+2, p[1]+0], - [p[0]+0, p[1]+2]] - - - for i in range(pot_adj_step.size()): - var next = pot_adj_step[i] - if(next[0]<0): - continue - if(next[0]>13): - continue - if(next[1]<0): - continue - if(next[1]>6): - continue - if(grid.used_grid[next[0]][next[1]]): - continue - if(grid._is_in_grid(Vector2(next[0], next[1])) ==false): - continue - if(grid.object_grid[next[0]][next[1]][0]!=Grid.Kind.WALL): - if(i==0): - continue - if(grid.object_grid[pot_adj_step[1][0]][pot_adj_step[1][1]][0]!=Grid.Kind.WALL && - grid.object_grid[pot_adj_step[3][0]][pot_adj_step[3][1]][0]!=Grid.Kind.WALL): - adj.append([STEP, Vector2(next[0],next[1]),1.1]) - continue - if(i==2): - continue - if(grid.object_grid[pot_adj_step[1][0]][pot_adj_step[1][1]][0]!=Grid.Kind.WALL && - grid.object_grid[pot_adj_step[4][0]][pot_adj_step[4][1]][0]!=Grid.Kind.WALL): - adj.append([STEP, Vector2(next[0],next[1]),1.1]) - continue - if(i==5): - continue - if(grid.object_grid[pot_adj_step[3][0]][pot_adj_step[3][1]][0]!=Grid.Kind.WALL && - grid.object_grid[pot_adj_step[6][0]][pot_adj_step[6][1]][0]!=Grid.Kind.WALL): - adj.append([STEP, Vector2(next[0],next[1]),1.1]) - continue - if(i==7): - continue - if(grid.object_grid[pot_adj_step[4][0]][pot_adj_step[4][1]][0]!=Grid.Kind.WALL && - grid.object_grid[pot_adj_step[6][0]][pot_adj_step[6][1]][0]!=Grid.Kind.WALL): - adj.append([STEP, Vector2(next[0],next[1]),1.1]) - continue - - adj.append([STEP, Vector2(next[0],next[1]),1.0]) - - - for i in range(pot_adj_roll.size()): - var next = pot_adj_roll[i] - if(next[0]<0): - continue - if(next[0]>13): - continue - if(next[1]<0): - continue - if(next[1]>6): - continue - if(grid.used_grid[next[0]][next[1]]): - continue - if(grid._is_in_grid(Vector2(next[0], next[1])) == false): - continue - if(grid.object_grid[next[0]][next[1]][0]!=Grid.Kind.WALL): - if(i==0): - if(grid.object_grid[next[0]+0][next[1]+1][0]!=Grid.Kind.WALL): - adj.append([ROLL, Vector2(next[0],next[1]),1.0]) - if(i==1): - if(grid.object_grid[next[0]+1][next[1]+0][0]!=Grid.Kind.WALL): - adj.append([ROLL, Vector2(next[0],next[1]),1.0]) - if(i==2): - if(grid.object_grid[next[0]-1][next[1]+0][0]!=Grid.Kind.WALL): - adj.append([ROLL, Vector2(next[0],next[1]),1.0]) - if(i==3): - if(grid.object_grid[next[0]+0][next[1]-1][0]!=Grid.Kind.WALL): - adj.append([ROLL, Vector2(next[0],next[1]),1.0]) - - return adj - - -func AStar(source, target): - #swap rtarget and source, when target source istr reached, do inversxse step - # node layout [g+h(x), g(x), current, from, kind] - var tmp = source - source = target - target = tmp - - var Q = PrioQueue.new() - Q.insert([0,0, [source[0], source[1]], [source[0], source[1]], NOTHING] ) - while !Q.empty(): - var node = Q.delMin() - - # Check if reached - if(node[2][0] == target[0] and node[2][1] == target[1]): - return [node[4], node[3]] # 4 is kind | 3 is from - - # Set flag - grid.used_grid[node[2][0]][node[2][1]] = true - var adj_list = adjacent(node[2]) - var current_field = node[2] - for i in adj_list: - var move_cost = i[2] - - var g_val = g_fn(node[1]+move_cost, i[1]) - var h_val = h_fn(i[1], target) - - #[g+h(x), g(x), current, from, kind] - var new_node = [g_val+h_val, g_val,i[1], node[2], i[0]] - Q.insert(new_node) - - return [NOTHING, [0,0]] func movement_calulcaotr(): var currentPosition = grid._pixel_to_grid_coords(global_position) - var enemyKind numbers = grid.countTargets(numbers) @@ -252,21 +123,17 @@ func movement_calulcaotr(): enemyKind = calcEnemyKind() actionKind = enemyKind actionFieldUsed = true + if(enemyKind==Grid.Kind.TERMINAL_SYMBOL): + return else: enemyKind = actionKind - if(enemyKind==Grid.Kind.TERMINAL_SYMBOL): - return - var targetField = grid.get_nearest(currentPosition, enemyKind) if(targetField==[-1,-1]): - return + return - - var MoveAdvice = getMoveDescription(currentPosition, targetField) - grid.reset_history() + return getMoveDescription(currentPosition, Vector2(targetField[0], targetField[1])) - return MoveAdvice func is_hittable(): var length = areaRefList.size() @@ -280,40 +147,28 @@ func hit_or_miss(target, current, delta): func movement_decider_ai(target, kindOfStep, delta): var currentPosition = grid._pixel_to_grid_coords(global_position) - if hitDelta >= hitTreshhold && randf() <0.5: - hitDelta -= hitTreshhold - var currentPixel = global_position - var hitPixelTarget = is_hittable() - - if hitPixelTarget!=null: - hit_or_miss(hitPixelTarget, currentPixel, delta*4) - return - + hitDelta -= hitTreshhold + var currentPixel = global_position + var hitPixelTarget = is_hittable() + + if hitPixelTarget!=null && randf()<0.5: + hit_or_miss(hitPixelTarget, currentPixel, delta*4) else: if(kindOfStep==STEP): run(Vector2(target[0]-currentPosition[0], target[1]-currentPosition[1]), delta*4) targetFieldCur = target - targetFieldUsed = true ai_movement_state = STEP elif(kindOfStep==ROLL): roll(Vector2(target[0]-currentPosition[0], target[1]-currentPosition[1]), delta*4) - targetFieldCur = target - targetFieldUsed = true + + targetFieldCur = target + targetFieldUsed = true + ExecutionState = EXECUTING -func movement_execution(delta): - - if hitDelta >= hitTreshhold && randf() <0.5: - hitDelta -= hitTreshhold - var currentPixel = global_position - var hitPixelTarget = is_hittable() - - if hitPixelTarget!=null: - hit_or_miss(hitPixelTarget, currentPixel, delta*4) - return - +func movement_execution(delta): if(targetFieldUsed): var cur = grid._pixel_to_grid_coords(global_position) var distance = sqrt(pow(cur[0]-targetFieldCur[0],2)+ pow(cur[1]-targetFieldCur[1],2)) @@ -340,21 +195,19 @@ func reset_exeution_state(delta): func makeMove(delta): - hitDelta += delta - aiDelta+=delta - if(aiDelta>aiTreshhold): - if ExecutionState == AI_MOVE: - threadDelta = 0 - var MoveAdvice = movement_calulcaotr() - if(MoveAdvice==null): - return - var target = MoveAdvice[1] - movement_decider_ai(target, MoveAdvice[0], delta) + lock.lock() + if ExecutionState == AI_MOVE: + threadDelta = 0 + var MoveAdvice = movement_calulcaotr() + if(MoveAdvice==null): + return + var target = MoveAdvice[1] + movement_decider_ai(target, MoveAdvice[0], delta) if ExecutionState == EXECUTING: movement_execution(delta) reset_exeution_state(delta) - + lock.unlock() # API Interface for ai_hero -> methods are handled in player.gd func attac(direction, delta): diff --git a/src/Player/Player.gd b/src/Player/Player.gd index 2abd01c..e50f982 100644 --- a/src/Player/Player.gd +++ b/src/Player/Player.gd @@ -150,24 +150,13 @@ func movement_hit(): func hit_finished(): grid._update_grid() movementState = moveState.IDLE - ai_movement_state = STEP - ExecutionState = EXECUTING + ExecutionState = AI_MOVE actionFieldUsed = false func movement_roll(): velocity = rollvector * ROLL_SPEED animation_state.change_state("roll") - - """ - # Roll.gd - func enter(): - owner.animation_state.travel("roll") - - func update(): - owner.velocity = rollvector * ROLL_SPEED - """ - ExecutionState = EXECUTING func roll_finished(): diff --git a/src/Player/Player.tscn b/src/Player/Player.tscn index be52078..a032e86 100644 --- a/src/Player/Player.tscn +++ b/src/Player/Player.tscn @@ -736,7 +736,7 @@ __meta__ = { } [node name="Stats" parent="." instance=ExtResource( 5 )] -max_health = 5 +max_health = 20 [node name="AnimationStates" type="Node" parent="."] script = ExtResource( 15 ) diff --git a/src/World.tscn b/src/World.tscn index 595ba10..d9487ac 100644 --- a/src/World.tscn +++ b/src/World.tscn @@ -21,7 +21,7 @@ region_enabled = true region_rect = Rect2( 0, 0, 1280, 720 ) [node name="Background" parent="." instance=ExtResource( 7 )] -frame = 41 +frame = 4 [node name="FloorTileMap" type="TileMap" parent="."] visible = false