From 7f8cd1a3bac3a4ac8637cc157e944feeacfbda03 Mon Sep 17 00:00:00 2001 From: Relintai Date: Wed, 15 Jul 2020 21:34:14 +0200 Subject: [PATCH] Mob visibility, pathing, targeting, and ai. Also small tweaks, and added a wait action. --- game/player/Body.gd | 240 +++++++++++++------------------ game/player/bs_entity_spawner.gd | 11 +- game/project.godot | 5 + game/scenes/World.gd | 63 ++++++-- game/scripts/ai/EntityAIGD.gd | 21 ++- 5 files changed, 182 insertions(+), 158 deletions(-) diff --git a/game/player/Body.gd b/game/player/Body.gd index 7983821..adb97d2 100644 --- a/game/player/Body.gd +++ b/game/player/Body.gd @@ -56,6 +56,8 @@ var visibility_update_timer : float = randi() var tile_size : int = 32 +var nameplate : Node + func _enter_tree() -> void: world = get_node(world_path) as Node2D tile_size = get_node("/root/Main").get_tile_size() @@ -77,99 +79,18 @@ func _enter_tree() -> void: transform = entity.get_transform_2d(true) -# set_physics_process(true) +func set_visibility(val : bool) -> void: + if val: + show() - -func _process(delta : float) -> void: - if entity.ai_state == EntityEnums.AI_STATE_OFF: - return + if nameplate: + nameplate.show() + elif !val: + hide() - visibility_update_timer += delta - - if visibility_update_timer < 1: - return - - visibility_update_timer = 0 + if nameplate: + nameplate.hide() - var vpos : Vector2 = -get_tree().root.canvas_transform.get_origin() + (get_tree().root.get_visible_rect().size / 2) - position - var l : float = vpos.length_squared() - var rs : float = get_tree().root.size.x * get_tree().root.size.x - rs *= 0.3 - - if l < rs: - if not visible: - show() -# set_physics_process(true) - else: - if visible: - hide() -# set_physics_process(false) - - -#func _physics_process(delta : float) -> void: -# if entity.sentity_data == null: -# return -# -# if dead: -# return -# -# if entity.c_is_controlled: -# process_movement(delta) -# else: -# if sleep: -# sleep_recheck_timer += delta -# -# if sleep_recheck_timer < 0.5: -# return -# -# sleep_recheck_timer = 0 -# -## if world != null: -## if not world.is_position_walkable(transform.origin): -## return -# -# process_movement(delta) - - -func process_movement(delta : float) -> void: - var state : int = entity.getc_state() - - if state & EntityEnums.ENTITY_STATE_TYPE_FLAG_ROOT != 0 or state & EntityEnums.ENTITY_STATE_TYPE_FLAG_STUN != 0: - return -# -# if (input_dir.length_squared() > 0.1): -# moving = true -## entity.moved() -# else: -# moving = false -# -# var hvel = vel -# -# var target = dir -# target *= entity.get_speed().ccurrent -# -# var accel -# if dir.dot(hvel) > 0: -# accel = ACCEL -# else: -# accel = DEACCEL -# -# hvel = hvel.linear_interpolate(target, accel*delta) -# vel = hvel -# vel = move_and_slide(vel) - -# if input_length > 0.1: -# #handle_graphic_facing(abs(dir.dot(Vector2(0, 1))) > 0.9) -# character_skeleton.update_facing(input_dir) -# -# character_skeleton.get_animation_tree().set("parameters/walking/blend_amount", input_dir.length()) -# -# if multiplayer.has_network_peer(): -# if not multiplayer.is_network_server(): -# rpc_id(1, "sset_position", position) -# else: -# sset_position(position) - func _unhandled_input(event: InputEvent) -> void: #Not sure why yet, but _unhandled_input gets called even after set_process_unhandled_input(false) if !entity.c_is_controlled: @@ -177,19 +98,28 @@ func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("left"): try_move(-1, 0) + get_tree().set_input_as_handled() return elif event.is_action_pressed("right"): try_move(1, 0) + get_tree().set_input_as_handled() return elif event.is_action_pressed("up"): try_move(0, -1) + get_tree().set_input_as_handled() return elif event.is_action_pressed("down"): try_move(0, 1) + get_tree().set_input_as_handled() + return + elif event.is_action_pressed("wait"): + world.player_moved() + get_tree().set_input_as_handled() return if event is InputEventMouseMotion and event.device != -1: - cmouseover(event) + cmouseover(event.position) + get_tree().set_input_as_handled() if event is InputEventMouseButton: if event.button_index == BUTTON_WHEEL_DOWN and event.device != -1: @@ -218,14 +148,22 @@ func _unhandled_input(event: InputEvent) -> void: if event.pressed and event.button_index == BUTTON_RIGHT and event.device != -1: target(event.position) + + get_tree().set_input_as_handled() if event is InputEventScreenTouch and event.pressed: target(event.position) + get_tree().set_input_as_handled() func try_move(dx, dy): + var state : int = entity.getc_state() + + if state & EntityEnums.ENTITY_STATE_TYPE_FLAG_ROOT != 0 or state & EntityEnums.ENTITY_STATE_TYPE_FLAG_STUN != 0: + return + var tp : Vector2 = get_tile_position() tp.x += dx tp.y += dy @@ -241,57 +179,81 @@ func try_move(dx, dy): if entity.c_is_controlled: world.player_moved() -func get_tile_position() -> Vector2: - var v : Vector2 = Vector2(int(transform.origin.x / tile_size), int(transform.origin.y / tile_size)) +func move_towards_target(): + var state : int = entity.getc_state() - return v + if state & EntityEnums.ENTITY_STATE_TYPE_FLAG_ROOT != 0 or state & EntityEnums.ENTITY_STATE_TYPE_FLAG_STUN != 0: + return + + var t : Entity = entity.getc_target() + + if !t: + return + + var bp : Vector2 = t.get_body().get_tile_position() + + var my_point = world.nav_graph.get_closest_point(get_tile_position()) + var target_point = world.nav_graph.get_closest_point(bp) + var path = world.nav_graph.get_point_path(my_point, target_point) + + if path: + assert(path.size() > 1) + + var move_tile = Vector2(path[1].x, path[1].y) + + if move_tile == bp: + return + + for e in world.enemies: + if e.get_body().get_tile_position() == move_tile: + return + + set_tile_position(move_tile) + +func get_tile_position() -> Vector2: + return Vector2(int(transform.origin.x / tile_size), int(transform.origin.y / tile_size)) func set_tile_position(pos : Vector2) -> void: transform.origin = pos * tile_size + Vector2(tile_size / 2, tile_size / 2) -func target(position : Vector2) -> bool: -# var space_state = get_world_2d().direct_space_state -# var results = space_state.intersect_point(world.make_canvas_position_local(position), 32, [], get_collision_layer()) -# #var results = space_state.intersect_point(position, 32, [], 2) -# -# if results: -# for result in results: -# if result.collider and result.collider.owner is Entity: -# entity.target_crequest_change((result.collider.owner as Node).get_path()) -# return true -# -# entity.target_crequest_change(NodePath()) -# else: -# entity.target_crequest_change(NodePath()) -# - return false +func target(position : Vector2) -> void: + #https://github.com/godotengine/godot/issues/32222 + position = position - get_viewport_transform().origin + position *= camera.zoom + + var pos : Vector2 = world.pixel_to_tile(position.x, position.y) + var enemy : Entity = world.get_enemy_at_tile(pos.x, pos.y) -func cmouseover(event): -# var space_state = get_world_2d().direct_space_state -# var results = space_state.intersect_point(world.make_canvas_position_local(position), 32, [], get_collision_layer()) -# #var results = space_state.intersect_point(position, 32, [], 2) -# -# if results: -# for result in results: -# if result.collider and result.collider.owner is Entity: -# var mo : Entity = result.collider.owner as Entity -# -# if last_mouse_over != null and last_mouse_over != mo: -# if is_instance_valid(last_mouse_over): -# last_mouse_over.notification_cmouse_exit() -# -# last_mouse_over = null -# -# if last_mouse_over == null: -# mo.notification_cmouse_enter() -# last_mouse_over = mo -# -# return -# -# if last_mouse_over != null: -# last_mouse_over.notification_cmouse_exit() -# last_mouse_over = null - pass + if enemy: + if entity.getc_target() != enemy: + entity.target_crequest_change(enemy.get_path()) + else: + entity.target_crequest_change(NodePath()) + +func cmouseover(position : Vector2): + #https://github.com/godotengine/godot/issues/32222 + position = position - get_viewport_transform().origin + position *= camera.zoom + + var pos : Vector2 = world.pixel_to_tile(position.x, position.y) + var enemy : Entity = world.get_enemy_at_tile(pos.x, pos.y) + + if enemy: + if last_mouse_over != null and last_mouse_over != entity: + if is_instance_valid(last_mouse_over): + last_mouse_over.notification_cmouse_exit() + + last_mouse_over = null + + if last_mouse_over == null: + entity.notification_cmouse_enter() + last_mouse_over = entity + + return + + if last_mouse_over != null: + last_mouse_over.notification_cmouse_exit() + last_mouse_over = null func on_c_controlled_changed(val): @@ -300,7 +262,6 @@ func on_c_controlled_changed(val): if val: camera = Camera2D.new() -# camera.zoom = Vector2(0.6, 0.6) camera.zoom = get_node("/root/Main").get_world_scale() add_child(camera) camera.current = true @@ -311,6 +272,8 @@ func on_c_controlled_changed(val): # set_process_input(true) set_process_unhandled_input(true) + + set_visibility(true) else: if camera: camera.queue_free() @@ -319,9 +282,10 @@ func on_c_controlled_changed(val): # set_process_input(false) set_process_unhandled_input(false) var nameplatescn : PackedScene = ResourceLoader.load("res://ui/nameplates/NamePlate.tscn") - var nameplate = nameplatescn.instance() + nameplate = nameplatescn.instance() get_parent().add_child(nameplate) + set_visibility(false) remote func sset_position(pposition : Vector2) -> void: diff --git a/game/player/bs_entity_spawner.gd b/game/player/bs_entity_spawner.gd index 21a3aa1..eca655f 100644 --- a/game/player/bs_entity_spawner.gd +++ b/game/player/bs_entity_spawner.gd @@ -70,7 +70,7 @@ remote func creceive_spawn_for(data: String, global_name : String, position: Vec ESS.request_entity_spawn(createinfo) # print("Player spawned ") - + return createinfo.created_entity remote func creceive_despawn_for(path : NodePath) -> void: @@ -93,7 +93,7 @@ puppet func spawn_owned_player(data : String, position : Vector3) -> Entity: ESS.request_entity_spawn(createinfo) # print("Player spawned ") - + return createinfo.created_entity func load_player(file_name : String, position : Vector3, network_owner : int) -> Entity: @@ -154,7 +154,7 @@ func spawn_display_player(file_name : String, node_path : NodePath) -> Entity: # print("Player spawned ") ESS.request_entity_spawn(createinfo) - + return createinfo.created_entity func spawn_networked_player(class_id : int, position : Vector3, name : String, node_name : String, sid : int) -> Entity: @@ -182,7 +182,7 @@ func spawn_networked_player(class_id : int, position : Vector3, name : String, createinfo.transform.origin = position ESS.request_entity_spawn(createinfo) - + # print("Player spawned " + str(createinfo)) return createinfo.created_entity @@ -211,7 +211,7 @@ func spawn_player(class_id : int, position : Vector3, name : String, node_name createinfo.networked = false ESS.request_entity_spawn(createinfo) - + # print("Player spawned " + str(createinfo)) return createinfo.created_entity @@ -272,6 +272,7 @@ func _request_entity_spawn(createinfo : EntityCreateInfo): get_scene_tree().root.get_node(createinfo.parent_path).add_child(entity_node) entity_node.setup(createinfo) + entity_node.set_maunal_process(true) createinfo.created_entity = entity_node diff --git a/game/project.godot b/game/project.godot index 71fca1a..20ba165 100644 --- a/game/project.godot +++ b/game/project.godot @@ -517,6 +517,11 @@ right={ "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null) ] } +wait={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":69,"unicode":0,"echo":false,"script":null) + ] +} [layer_names] diff --git a/game/scenes/World.gd b/game/scenes/World.gd index 9379c34..5e96ed5 100644 --- a/game/scenes/World.gd +++ b/game/scenes/World.gd @@ -46,6 +46,7 @@ export(int) var enemy_count : int = 14 var map : Array = [] var rooms : Array = [] var enemies : Array = [] +var nav_graph : AStar2D onready var tile_map : TileMap = $Terrarin onready var visibility_map : TileMap = $VisibilityMap @@ -59,9 +60,6 @@ func load_character(file_name: String) -> void: randomize() build_level() - -# if spawn_mobs: -# generate() #Place player var start_room = rooms.front() @@ -97,6 +95,11 @@ func load_character(file_name: String) -> void: call_deferred("update_visibility") func player_moved(): + _player.update(1) + + for e in enemies: + e.update(1) + call_deferred("update_visibility") func update_visibility(): @@ -123,6 +126,42 @@ func update_visibility(): if !occlusion || (occlusion.position - test_point).length() < 1: visibility_map.set_cell(x, y, -1) + + for e in enemies: + var b = e.get_body() + + if !b.visible: + var pos : Vector2 = b.transform.origin + + var occlusion = space_state.intersect_ray(body.transform.origin, pos) + + if !occlusion: + b.set_visibility(true) + e.sets_target(_player) + +func clear_path(tile): + var new_point = nav_graph.get_available_point_id() + nav_graph.add_point(new_point, Vector2(tile.x, tile.y)) + + var points_to_conect = [] + + if tile.x > 0 && map[tile.x - 1][tile.y] == Tile.Floor: + points_to_conect.append(nav_graph.get_closest_point(Vector2(tile.x - 1, tile.y))) + + if tile.y > 0 && map[tile.x][tile.y - 1] == Tile.Floor: + points_to_conect.append(nav_graph.get_closest_point(Vector2(tile.x, tile.y - 1))) + + if tile.x < 0 && map[tile.x + 1][tile.y] == Tile.Floor: + points_to_conect.append(nav_graph.get_closest_point(Vector2(tile.x + 1, tile.y))) + + if tile.y < 0 && map[tile.x][tile.y + 1] == Tile.Floor: + points_to_conect.append(nav_graph.get_closest_point(Vector2(tile.x, tile.y + 1))) + + for point in points_to_conect: + nav_graph.connect_points(point, new_point) + +func pixel_to_tile(x, y): + return tile_map.world_to_map(Vector2(x, y)) func tile_to_pixel_center(x, y): return Vector2((x + 0.5) * tile_size, (y + 0.5) * tile_size) @@ -141,6 +180,14 @@ func is_position_walkable(x : int, y : int) -> bool: return false return true + +func get_enemy_at_tile(x : int, y : int) -> Entity: + for e in enemies: + var pos : Vector2 = e.get_body().get_tile_position() + if pos.x == x && pos.y == y: + return e + + return null func build_level(): rooms.clear() @@ -152,6 +199,8 @@ func build_level(): enemies.clear() + nav_graph = AStar2D.new() + for x in range(level_size.x): map.append([]) for y in range(level_size.y): @@ -373,11 +422,9 @@ func cut_regions(free_regions, region_to_remove): func set_tile(x, y, type): map[x][y] = type tile_map.set_cell(x, y, type) - -func generate() -> void: - for x in range(-5, 5): - for y in range(-5, 5): - ESS.entity_spawner.spawn_mob(1, 50, Vector3(x * 200, y * 200, 0)) + + if type == Tile.Floor: + clear_path(Vector2(x, y)) func save() -> void: if _player == null or _player_file_name == "": diff --git a/game/scripts/ai/EntityAIGD.gd b/game/scripts/ai/EntityAIGD.gd index 8147b61..35da154 100644 --- a/game/scripts/ai/EntityAIGD.gd +++ b/game/scripts/ai/EntityAIGD.gd @@ -36,6 +36,8 @@ func _on_set_owner(): if not is_instance_valid(owner): return + owner.connect("starget_changed", self, "starget_changed") + if not owner.sentity_data: return @@ -76,7 +78,9 @@ func attack(delta): if target == null: owner.ai_state = EntityEnums.AI_STATE_REGENERATE - owner.get_body().target_movement_direction = Vector2() + return + + if owner.cast_is_castings(): return var cast : bool = false @@ -116,19 +120,22 @@ func attack(delta): break if owner.cast_is_castings(): - owner.get_body().target_movement_direction = Vector2() return - owner.get_body().target_movement_direction = Vector2() - - var dir : Vector2 = target.get_body().position - owner.get_body().position + var dir : Vector2 = target.get_body().get_tile_position() - owner.get_body().get_tile_position() var l = dir.length() - if l > 2.5: - owner.get_body().target_movement_direction = Vector2(dir.x, dir.y) + if l > 1: + owner.get_body().move_towards_target() func sort_spells_by_rank(a, b): if a == null or b == null: return true return a["rank"] > b["rank"] + +func starget_changed(entity: Entity, old_target: Entity): + if entity: + owner.ai_state = EntityEnums.AI_STATE_ATTACK + else: + owner.ai_state = EntityEnums.AI_STATE_OFF