Compare commits
31 Commits
Author | SHA1 | Date |
---|---|---|
|
3b4d8a4d8e | |
|
3d716ed06d | |
|
f3f4cef1bf | |
|
263816fa53 | |
|
08647a4188 | |
|
7a18765ebd | |
|
da1fdf10d0 | |
|
624c14150e | |
|
a75eb4aa1d | |
|
28c9b857bd | |
|
2e1559d96e | |
|
9822c716f2 | |
|
b187b96507 | |
|
fcbd269f68 | |
|
b3220f968d | |
|
dc3ead30b4 | |
|
d026c07804 | |
|
4b8479e87e | |
|
d8bc800077 | |
|
0becddf3d2 | |
|
cd01fd24a2 | |
|
57db80e685 | |
|
268f2c0ce1 | |
|
4763de9827 | |
|
238af1cc83 | |
|
cd82cf5bdf | |
|
2923738906 | |
|
8bbca6cef5 | |
|
7304b48f3c | |
|
9446c10556 | |
|
731ff9ed12 |
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"godotTools.editorPath.godot4": "g:\\Godot\\Godot_v4.3-stable_win64_console.exe"
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 474 KiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://di16cmfomo60u"
|
||||
path="res://.godot/imported/intro.png-5733d5421d999e0273f5c7e8c62d2491.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/intro.png"
|
||||
dest_files=["res://.godot/imported/intro.png-5733d5421d999e0273f5c7e8c62d2491.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
|
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cr4e1faypphkg"
|
||||
path="res://.godot/imported/ingame-instructions.png-47b6e281450dc0b79c6fd2f40f059336.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/ingame-instructions.png"
|
||||
dest_files=["res://.godot/imported/ingame-instructions.png-47b6e281450dc0b79c6fd2f40f059336.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
|
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cynull1bsik42"
|
||||
path="res://.godot/imported/outro.png-474f491bb8e2fbb9b182fa83ab072d54.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/outro.png"
|
||||
dest_files=["res://.godot/imported/outro.png-474f491bb8e2fbb9b182fa83ab072d54.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
|
Binary file not shown.
|
@ -1 +1 @@
|
|||
{"frames":[{"duration":0.1}],"height":320,"filename":"tilemaps.aseprite","layers":[{"name":"ground","cels":[{"frame":0,"image":"tilemaps/tilemap_ground.png"}]},{"name":"objects","cels":[{"frame":0,"image":"tilemaps/tilemap_objects.png"}]},{"name":"temperature","cels":[{"frame":0,"image":"tilemaps/tilemap_temperature.png"}]},{"name":"player","cels":[{"frame":0,"image":"tilemaps/tilemap_player.png"}]}],"width":320}
|
||||
{"filename":"tilemaps.aseprite","width":320,"frames":[{"duration":0.1}],"layers":[{"cels":[{"frame":0,"image":"tilemaps\\tilemap_ground.png"}],"name":"ground"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_objects.png"}],"name":"objects"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_temperature.png"}],"name":"temperature"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_player.png"}],"name":"player"}],"height":320}
|
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -16,19 +16,36 @@ texture = ExtResource("1_ukrsa")
|
|||
3:0/0/custom_data_0 = true
|
||||
3:0/0/custom_data_2 = 1
|
||||
3:0/8 = 8
|
||||
3:0/8/custom_data_0 = true
|
||||
3:0/8/custom_data_2 = 1
|
||||
1:2/0 = 0
|
||||
3:2/next_alternative_id = 5
|
||||
3:2/next_alternative_id = 7
|
||||
3:2/0 = 0
|
||||
3:2/0/custom_data_0 = true
|
||||
3:2/0/custom_data_2 = 1
|
||||
3:2/4 = 4
|
||||
3:2/4/custom_data_2 = 1
|
||||
0:0/0 = 0
|
||||
0:0/0/custom_data_0 = true
|
||||
0:0/0/custom_data_2 = 4
|
||||
3:1/next_alternative_id = 3
|
||||
0:0/0/custom_data_2 = 2
|
||||
3:1/next_alternative_id = 4
|
||||
3:1/0 = 0
|
||||
3:1/0/custom_data_0 = true
|
||||
3:1/0/custom_data_2 = 1
|
||||
3:1/2 = 2
|
||||
4:0/0 = 0
|
||||
3:1/2/custom_data_0 = true
|
||||
3:1/2/custom_data_2 = 1
|
||||
5:3/0 = 0
|
||||
5:3/0/custom_data_0 = true
|
||||
4:0/0 = 0
|
||||
4:0/0/custom_data_0 = true
|
||||
4:0/0/custom_data_2 = 3
|
||||
0:1/0 = 0
|
||||
0:1/0/custom_data_0 = true
|
||||
0:1/0/custom_data_2 = 8
|
||||
0:2/0 = 0
|
||||
0:2/0/custom_data_0 = true
|
||||
0:2/0/custom_data_2 = 400
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_x77e4"]
|
||||
texture = ExtResource("2_15xge")
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -76,16 +76,6 @@ key_9={
|
|||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":57,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
force_game_tick={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
force_game_tick_fast={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":71,"key_label":0,"unicode":103,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
key_4={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"location":0,"echo":false,"script":null)
|
||||
|
@ -106,9 +96,35 @@ key_6={
|
|||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":54,"key_label":0,"unicode":54,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
force_game_tick={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":71,"key_label":0,"unicode":103,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
force_game_tick_fast={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
auto_tick={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_graph_edit={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_temperature_layer={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[rendering]
|
||||
|
||||
textures/canvas_textures/default_texture_filter=0
|
||||
renderer/rendering_method="gl_compatibility"
|
||||
renderer/rendering_method.mobile="gl_compatibility"
|
||||
environment/defaults/default_clear_color=Color(0.356863, 0.431373, 0.882353, 1)
|
||||
|
|
|
@ -7,8 +7,8 @@ extends Camera2D
|
|||
@export var max_speed: float = 500.0
|
||||
@export var inner_border_threshold: float = 0.0 # 60.0
|
||||
@export var outer_border_threshold: float = 0.0 # 40.0
|
||||
@export var min_position: Vector2 = Vector2(0, 0)
|
||||
@export var max_position: Vector2 = Vector2(1375, 660)
|
||||
@export var min_position: Vector2 = Vector2(874, 843)
|
||||
@export var max_position: Vector2 = Vector2(4884, 4623)
|
||||
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
#
|
||||
|
|
|
@ -8,14 +8,22 @@ enum Event {
|
|||
CAMP_ADDED_ITEM,
|
||||
CAMP_TAKEN_ITEM,
|
||||
CAMP_TAKE_ITEM_FAILED,
|
||||
CAMP_BOAT_PART_DELIVERED,
|
||||
CAMP_BOAT_COMPLETE,
|
||||
SLEEP,
|
||||
PLAYER_PICKED_UP_ITEM,
|
||||
PLAYER_DROPPED_ITEM,
|
||||
PLAYER_USED_ITEM,
|
||||
GAME_STATE_WIN,
|
||||
NEW_EXPLORATION_GOAL,
|
||||
EXPLORATION_GOAL_REACHED,
|
||||
EXPLORATION_GOAL_CLOSE_ENOUGH,
|
||||
TEMPERATURE_COLD,
|
||||
TIME_SUNDOWN,
|
||||
};
|
||||
#
|
||||
static var events: Array[TrackedEvent] = []
|
||||
static var max_events: int = 20
|
||||
static var max_events: int = 14
|
||||
static var callbacks: Array[Callable] = []
|
||||
|
||||
|
||||
|
@ -54,20 +62,39 @@ static func populate_visual_log_create_label(event: TrackedEvent, container: Con
|
|||
elif event_id == Event.CAMP_ADDED_ITEM:
|
||||
text = "Camp added item"
|
||||
elif event_id == Event.CAMP_TAKEN_ITEM:
|
||||
text = "Camp taken item"
|
||||
text = "Camp taken item x" + str(params["count"])
|
||||
elif event_id == Event.CAMP_TAKE_ITEM_FAILED:
|
||||
text = "Could not take item from camp"
|
||||
elif event_id == Event.SLEEP:
|
||||
text = "Player slept"
|
||||
elif event_id == Event.PLAYER_PICKED_UP_ITEM:
|
||||
text = "took"
|
||||
text = "Took"
|
||||
elif event_id == Event.PLAYER_DROPPED_ITEM:
|
||||
text = "dropped"
|
||||
text = "Dropped"
|
||||
elif event_id == Event.PLAYER_USED_ITEM:
|
||||
text = "used"
|
||||
text = "Used"
|
||||
elif event_id == Event.CAMP_BOAT_PART_DELIVERED:
|
||||
text = "Boat construction"
|
||||
elif event_id == Event.CAMP_BOAT_COMPLETE:
|
||||
text = "Boat complete"
|
||||
elif event_id == Event.GAME_STATE_WIN:
|
||||
text = "Game won"
|
||||
elif event_id == Event.NEW_EXPLORATION_GOAL:
|
||||
text = "New goal " + str(params["goal"])
|
||||
elif event_id == Event.EXPLORATION_GOAL_REACHED:
|
||||
text = "Goal reached"
|
||||
elif event_id == Event.EXPLORATION_GOAL_CLOSE_ENOUGH:
|
||||
text = "Got close enough to goal..."
|
||||
elif event_id == Event.TEMPERATURE_COLD:
|
||||
text = "Temperature is cold: -" + str(params["temperature"])
|
||||
elif event_id == Event.TIME_SUNDOWN:
|
||||
text = "The sun is setting..."
|
||||
else:
|
||||
text = "Something happened..."
|
||||
|
||||
var event_label: Label = Label.new()
|
||||
event_label.text = text
|
||||
event_label.add_theme_font_size_override("font_size", 24 * game_manager.calculate_scale(Vector2(1200, 1200)).y)
|
||||
event_label.add_theme_color_override("font_color", Color(0, 0, 0))
|
||||
|
||||
var event_container: HBoxContainer = HBoxContainer.new()
|
||||
|
@ -78,6 +105,8 @@ static func populate_visual_log_create_label(event: TrackedEvent, container: Con
|
|||
if item_texture:
|
||||
var item_texture_rect: TextureRect = TextureRect.new()
|
||||
item_texture_rect.texture = item_texture
|
||||
item_texture_rect.set_expand_mode(TextureRect.EXPAND_FIT_WIDTH)
|
||||
item_texture_rect.set_stretch_mode(TextureRect.STRETCH_KEEP_ASPECT_CENTERED)
|
||||
event_container.add_child(item_texture_rect)
|
||||
|
||||
container.add_child(event_container)
|
||||
|
|
|
@ -8,13 +8,21 @@ var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
|||
@onready var camera: CameraController = $Camera2D as CameraController
|
||||
@onready var game_ticker: Timer = $GameTick
|
||||
#
|
||||
@onready var health_bar: ProgressBar = $Camera2D/CanvasLayer/VBoxContainer/HealthBar
|
||||
@onready var food_bar: ProgressBar = $Camera2D/CanvasLayer/VBoxContainer/FoodBar
|
||||
@onready var temperature_bar: ProgressBar = $Camera2D/CanvasLayer/VBoxContainer/TemperatureBar
|
||||
@onready var temperature_layer: Node2D = $Tileset/TemperatureLayer
|
||||
@onready var health_bar: ProgressBar = %HealthBar
|
||||
@onready var food_bar: ProgressBar = %FoodBar
|
||||
@onready var temperature_bar: ProgressBar = %TemperatureBar
|
||||
@onready var temperature_resistance_bar: ProgressBar = %TemperatureResistanceBar
|
||||
@onready var time_of_day_bar: ProgressBar = %TimeOfDayBar
|
||||
|
||||
var tilemap_navigation: TilemapNavigation = TilemapNavigation.new()
|
||||
|
||||
@onready var tree_visualizer: BehaviorTreeVisualizer = %TreeVisualizer
|
||||
|
||||
#
|
||||
var waiting_for_input: bool = true
|
||||
|
||||
@onready var intro_image: Sprite2D = $Camera2D/IntroImage
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
tilemap_navigation.world = world
|
||||
|
@ -24,27 +32,71 @@ func _ready() -> void:
|
|||
world.step_visualizer.game_manager = self
|
||||
world.step_visualizer.world = world
|
||||
update_bars()
|
||||
call_deferred("defer_ready")
|
||||
|
||||
|
||||
# game_ticker.start()
|
||||
func defer_ready() -> void:
|
||||
tree_visualizer.behavior_tree = player.behavior_tree
|
||||
tree_visualizer.build_tree()
|
||||
|
||||
intro_image.visible = true
|
||||
await wait_for_key_press()
|
||||
get_tree().create_tween().tween_method(set_intro_opacity, 1.0, 0.0, 1.0)
|
||||
get_tree().create_tween().tween_method(set_instructions_opacity, 0.0, 1.0, 1.0)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("key_1"):
|
||||
camera.go_to_zooming(Vector2(517.469787597656, 289.846008300781), 1.771561)
|
||||
if Input.is_action_just_pressed("key_2"):
|
||||
camera.go_to_zooming(Vector2(789.883972167969, 450.102813720703), 0.56015348434448)
|
||||
if Input.is_action_just_pressed("key_9"):
|
||||
world.camp_manager.campfire_light()
|
||||
world.camp_manager.sleep_effect()
|
||||
world.camp_manager.campfire_extinguish()
|
||||
if Input.is_action_just_pressed("force_game_tick"):
|
||||
Task.print_behavior_tree_evaluation = true
|
||||
_on_game_tick_timeout()
|
||||
Task.print_behavior_tree_evaluation = false
|
||||
if Input.is_action_pressed("force_game_tick_fast"):
|
||||
_on_game_tick_timeout()
|
||||
if Input.is_action_just_pressed("key_6"):
|
||||
if Input.is_action_just_pressed("toggle_temperature_layer"):
|
||||
toggle_temperature_layer()
|
||||
camera.print_config()
|
||||
if Input.is_action_just_pressed("auto_tick"):
|
||||
if game_ticker.is_stopped():
|
||||
game_ticker.start()
|
||||
else:
|
||||
game_ticker.stop()
|
||||
if Input.is_action_just_pressed("key_1"):
|
||||
get_tree().reload_current_scene()
|
||||
if Input.is_action_just_pressed("key_2"):
|
||||
player.exploration_task.current_goal = world.tilemap_ground.local_to_cell(world.get_local_mouse_position())
|
||||
player.behavior_tree.blackboard["cached_paths"] = {}
|
||||
player.behavior_tree.blackboard["path"] = []
|
||||
if Input.is_action_just_pressed("key_3"):
|
||||
player.board_position = Vector2i(world.camp_manager.camp)
|
||||
player.board_position.y += 1
|
||||
|
||||
if intro_image.is_visible():
|
||||
intro_image.set_scale(calculate_scale(intro_image.texture.get_size()))
|
||||
|
||||
|
||||
func calculate_scale(image_size: Vector2) -> Vector2:
|
||||
var viewport_size: Vector2 = world.get_viewport_rect().size
|
||||
var scale: float = viewport_size.x / image_size.x
|
||||
return Vector2(scale, scale)
|
||||
|
||||
|
||||
# SECTION: intro / outro
|
||||
|
||||
func set_intro_opacity(opacity: float) -> void:
|
||||
intro_image.set_modulate(Color(1, 1, 1, opacity))
|
||||
|
||||
|
||||
func set_instructions_opacity(opacity: float) -> void:
|
||||
%InstructionsRect.set_modulate(Color(1, 1, 1, opacity))
|
||||
%InstructionsRect.show()
|
||||
|
||||
|
||||
func set_outro_opacity(opacity: float) -> void:
|
||||
%OutroImageContainer.set_modulate(Color(1, 1, 1, opacity))
|
||||
%OutroImageContainer.show()
|
||||
|
||||
|
||||
# SECTION: game tick
|
||||
|
||||
func player_health_depleted():
|
||||
# TODO
|
||||
|
@ -53,43 +105,191 @@ func player_health_depleted():
|
|||
|
||||
func _on_game_tick_timeout() -> void:
|
||||
var timer_on_game_tick_timeout: PerformanceTimer = PerformanceTimer.new()
|
||||
timer_on_game_tick_timeout.display_name = "game tick duration"
|
||||
timer_on_game_tick_timeout.display_name = "frame"
|
||||
|
||||
tilemap_navigation.game_tick_start()
|
||||
world.game_tick_start()
|
||||
|
||||
player.game_tick()
|
||||
|
||||
apply_player_exploration_distance()
|
||||
|
||||
tree_visualizer.update_task_statuses(player.behavior_tree.blackboard)
|
||||
|
||||
tilemap_navigation.game_tick_end()
|
||||
world.game_tick_end()
|
||||
EventsTracker.populate_visual_log(%RecentEventsLog, self)
|
||||
|
||||
update_bars()
|
||||
world.camp_manager.populate_camp_visualization(%BoatProcessUI, %CampItemUI)
|
||||
handle_result_game_state(player.behavior_tree.blackboard)
|
||||
|
||||
if not game_ticker.is_stopped():
|
||||
camera_follow_player()
|
||||
|
||||
timer_on_game_tick_timeout.stop()
|
||||
|
||||
|
||||
func camera_follow_player() -> void:
|
||||
var player_position: Vector2 = world.tilemap_player.cell_to_local(player.board_position)
|
||||
var targeted_position = null
|
||||
|
||||
if player.behavior_tree.blackboard.has("path"):
|
||||
var path: Array = player.behavior_tree.blackboard["path"]
|
||||
if path.size() > 0:
|
||||
targeted_position = world.tilemap_player.cell_to_local(path[path.size() - 1])
|
||||
|
||||
if not targeted_position:
|
||||
camera.go_to(player_position)
|
||||
return
|
||||
|
||||
var avg_position = (player_position + targeted_position) / 2
|
||||
var distance: float = player_position.distance_to(targeted_position)
|
||||
var zoom_level: float
|
||||
if distance < 200:
|
||||
zoom_level = distance_to_zoom_level(200)
|
||||
else:
|
||||
zoom_level = distance_to_zoom_level(distance)
|
||||
|
||||
avg_position.x += distance / 2
|
||||
|
||||
camera.go_to_zooming(avg_position, zoom_level)
|
||||
|
||||
|
||||
func apply_player_exploration_distance():
|
||||
player.exploration_task.closest_distance_to_goal = min(player.exploration_task.closest_distance_to_goal, TilemapNavigation.manhattan_distance(player.board_position, player.exploration_task.current_goal))
|
||||
|
||||
|
||||
func distance_to_zoom_level(distance: float) -> float:
|
||||
var a: float = 862.08
|
||||
var b: float = 274.13
|
||||
return a / (distance + b)
|
||||
|
||||
|
||||
func handle_result_game_state(blackboard: Dictionary) -> void:
|
||||
if blackboard.has("game_state_win"):
|
||||
EventsTracker.track(EventsTracker.Event.GAME_STATE_WIN)
|
||||
game_ticker.stop()
|
||||
get_tree().create_tween().tween_method(set_outro_opacity, 0.0, 1.0, 1.0)
|
||||
|
||||
|
||||
func update_boat_progress_old() -> void:
|
||||
var part_counts: Dictionary = { # @formatter:off
|
||||
tilemap_types.OBJECT_I_BOAT_PART_ENGINE: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_FUEL: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_ANCHOR: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_CHEST: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_GEARS: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_MEDIKIT: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_PADDLE: 0,
|
||||
tilemap_types.OBJECT_I_BOAT_PART_GAS_STOVE: 0
|
||||
} # @formatter:on
|
||||
|
||||
for boat_part in world.camp_manager.boat_items:
|
||||
if part_counts.has(boat_part):
|
||||
part_counts[boat_part] += 1
|
||||
|
||||
for part in part_counts.keys():
|
||||
var count = part_counts[part]
|
||||
if count > 0:
|
||||
if part == tilemap_types.OBJECT_I_BOAT_PART_ENGINE:
|
||||
%BoatPartEngine.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_ENGINE)
|
||||
%BoatPartEngine.visible = true
|
||||
%EngineCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_FUEL:
|
||||
%BoatPartFuel.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_FUEL)
|
||||
%BoatPartFuel.visible = true
|
||||
%FuelCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_ANCHOR:
|
||||
%BoatPartAnchor.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_ANCHOR)
|
||||
%BoatPartAnchor.visible = true
|
||||
%AnchorCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_CHEST:
|
||||
%BoatPartChest.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_CHEST)
|
||||
%BoatPartChest.visible = true
|
||||
%ChestCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_GEARS:
|
||||
%BoatPartGears.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_GEARS)
|
||||
%BoatPartGears.visible = true
|
||||
%GearsCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_MEDIKIT:
|
||||
%BoatPartMedikit.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_MEDIKIT)
|
||||
%BoatPartMedikit.visible = true
|
||||
%MedikitCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_PADDLE:
|
||||
%BoatPartPaddle.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_PADDLE)
|
||||
%BoatPartPaddle.visible = true
|
||||
%PaddleCount.text = str(count)
|
||||
elif part == tilemap_types.OBJECT_I_BOAT_PART_GAS_STOVE:
|
||||
%BoatPartGasStove.texture = world.tilemap_interactive.get_cell_texture(tilemap_types.OBJECT_I_BOAT_PART_GAS_STOVE)
|
||||
%BoatPartGasStove.visible = true
|
||||
%StoveCount.text = str(count)
|
||||
else:
|
||||
push_error("Unknown boat part: " + str(part))
|
||||
|
||||
|
||||
func update_bars() -> void:
|
||||
if health_bar != null:
|
||||
health_bar.max_value = player.max_health
|
||||
health_bar.value = clamp(player.health, 0, player.max_health)
|
||||
$Camera2D/CanvasLayer/VBoxContainer/HealthBar/HealthLabel.text = str(health_bar.value) + "/" + str(player.max_health)
|
||||
$Camera2D/CanvasLayer/VBoxContainer/HealthBar/HealthLabel.add_theme_color_override("font_color", Color(1, 1, 1))
|
||||
%HealthLabel.text = str(health_bar.value) + "/" + str(player.max_health)
|
||||
%HealthLabel.add_theme_color_override("font_color", Color(1, 1, 1))
|
||||
|
||||
if food_bar != null:
|
||||
food_bar.max_value = player.max_food
|
||||
food_bar.value = clamp(player.food, 0, player.max_food)
|
||||
$Camera2D/CanvasLayer/VBoxContainer/FoodBar/FoodLabel.text = str(food_bar.value) + "/" + str(player.max_food)
|
||||
%FoodLabel.text = str(food_bar.value) + "/" + str(player.max_food)
|
||||
|
||||
if temperature_resistance_bar != null:
|
||||
temperature_resistance_bar.max_value = player.temperature_set_buff_value
|
||||
temperature_resistance_bar.value = clamp(player.temperature_buff_timer, 0, player.temperature_set_buff_value)
|
||||
%TemperatureResistanceLabel.text = str(temperature_resistance_bar.value) + "/" + str(player.temperature_set_buff_value)
|
||||
|
||||
if temperature_bar != null:
|
||||
temperature_bar.max_value = player.temperature_set_buff_value
|
||||
temperature_bar.value = clamp(player.temperature_buff_timer, 0, player.temperature_set_buff_value)
|
||||
$Camera2D/CanvasLayer/VBoxContainer/TemperatureBar/TemperatureLabel.text = str(temperature_bar.value) + "/" + str(player.temperature_set_buff_value)
|
||||
temperature_bar.max_value = player.temperature_endure
|
||||
# invert the value to show the time left
|
||||
var countdown: int = player.temperature_endure - player.temperature_timer
|
||||
temperature_bar.value = clamp(countdown, 0, player.temperature_endure)
|
||||
%TemperatureLabel.text = str(temperature_bar.value) + "/" + str(player.temperature_endure)
|
||||
|
||||
if time_of_day_bar != null:
|
||||
time_of_day_bar.max_value = 1
|
||||
time_of_day_bar.value = float(world.camp_manager.time_of_day) / world.camp_manager.day_length
|
||||
time_of_day_bar.self_modulate = calculate_time_of_day_color(world.camp_manager.time_of_day, world.camp_manager.day_length)
|
||||
%TimeOfDayLabel.text = calculate_display_time_of_day(world.camp_manager.time_of_day, world.camp_manager.day_length)
|
||||
|
||||
|
||||
func calculate_display_time_of_day(current_time: int, day_length: int) -> String:
|
||||
# format as 24 hour clock, start at 06:00 and end at 21:00
|
||||
var start: int = 6
|
||||
var end: int = 21
|
||||
var hours_per_day: int = end - start
|
||||
var time_of_day: float = float(current_time) / day_length * hours_per_day + start
|
||||
var hours: int = int(time_of_day)
|
||||
var minutes: int = int((time_of_day - hours) * 60)
|
||||
hours %= 24
|
||||
return str(hours).pad_zeros(2) + ":" + str(minutes).pad_zeros(2)
|
||||
|
||||
|
||||
func calculate_time_of_day_color(current_time: int, day_length: int) -> Color:
|
||||
var start: Color = Color(1, 1, 0)
|
||||
var end: Color = Color(1, 0, 0)
|
||||
var progress: float = float(current_time) / day_length
|
||||
progress = clamp(progress, 0, 1)
|
||||
progress = pow(progress, 2)
|
||||
return start.lerp(end, progress)
|
||||
|
||||
|
||||
func toggle_temperature_layer() -> void:
|
||||
if temperature_layer != null:
|
||||
temperature_layer.visible = not temperature_layer.visible
|
||||
print("TemperatureLayer visibility:", temperature_layer.visible)
|
||||
else:
|
||||
print("TemperatureLayer is null!")
|
||||
world.tilemap_temperature.tilemap.visible = not world.tilemap_temperature.tilemap.visible
|
||||
|
||||
|
||||
func wait_for_key_press():
|
||||
waiting_for_input = true
|
||||
while waiting_for_input:
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventKey and event.pressed:
|
||||
waiting_for_input = false
|
||||
|
|
|
@ -3,12 +3,12 @@ extends Node
|
|||
|
||||
@export var max_health: int = 100
|
||||
# food system
|
||||
@export var max_food: int = 100
|
||||
@export var max_food: int = 250
|
||||
@export var food_damage: int = 1
|
||||
@export var food_addon_per_berry: int = 100
|
||||
@export var food_critical_threshold: int = 50
|
||||
@export var food_base_threshold: int = 150
|
||||
# temperature
|
||||
@export var temperature_set_buff_value: int = 50
|
||||
@export var temperature_set_buff_value: int = 150
|
||||
@export var temperature_damage: int = 1
|
||||
@export var temperature_endure: int = 50
|
||||
# viewing
|
||||
|
@ -27,18 +27,20 @@ var board_position: Vector2i = Vector2i(0, 0):
|
|||
|
||||
@onready var behavior_tree: BehaviorTree = $BehaviorTree
|
||||
|
||||
var exploration_task: TaskPlannedExploration = null
|
||||
var food: int = max_food
|
||||
# var water: int = 0
|
||||
var temperature_buff_timer: int = 0
|
||||
var temperature_timer: int = 0
|
||||
var health: int = max_health
|
||||
|
||||
#
|
||||
var inventory_slot: Vector2i = tilemap_types.EMPTY:
|
||||
set(value):
|
||||
inventory_slot = value
|
||||
update_board()
|
||||
|
||||
var player_memory: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("defer_ready")
|
||||
|
@ -52,10 +54,11 @@ func defer_ready() -> void:
|
|||
else:
|
||||
push_error("No player start position found on tilemap")
|
||||
update_board()
|
||||
exploration_task = behavior_tree.find_task_by_name("TaskPlannedExploration")
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("key_3"):
|
||||
if Input.is_action_just_pressed("key_1"):
|
||||
game_manager.camera.go_to_zooming(game_manager.world.tilemap_player.cell_to_local(board_position), 2)
|
||||
if Input.is_action_just_pressed("key_5"):
|
||||
pick_up_item(Vector2i(5, 8))
|
||||
|
@ -90,13 +93,15 @@ func pick_up_item(tilemap_pos: Vector2i) -> void:
|
|||
|
||||
var pick_up_item_type: Vector2i = game_manager.world.tilemap_interactive.tilemap.get_cell_atlas_coords(tilemap_pos)
|
||||
|
||||
# check if tile will transform into another tile upon pickup
|
||||
# check if inventory contains item that needs to be transformed on dropping
|
||||
# this should never be the case, as the pick up item operation should already reflect this transformation
|
||||
var tile_drop_item: Vector2i = inventory_slot
|
||||
if tile_drop_item == tilemap_types.OBJECT_I_FILLED_BUSH:
|
||||
tile_drop_item = tilemap_types.OBJECT_I_BERRY
|
||||
elif tile_drop_item == tilemap_types.OBJECT_I_TREE_FULL:
|
||||
tile_drop_item = tilemap_types.OBJECT_I_STICK
|
||||
|
||||
# check if tile will transform into another tile upon pickup
|
||||
var tile_after_pickup_transform = null
|
||||
if pick_up_item_type == tilemap_types.OBJECT_I_FILLED_BUSH:
|
||||
tile_after_pickup_transform = tilemap_types.OBJECT_I_EMPTY_BUSH
|
||||
|
@ -104,7 +109,6 @@ func pick_up_item(tilemap_pos: Vector2i) -> void:
|
|||
elif pick_up_item_type == tilemap_types.OBJECT_I_TREE_FULL:
|
||||
tile_after_pickup_transform = tilemap_types.OBJECT_I_TREE_CUT
|
||||
pick_up_item_type = tilemap_types.OBJECT_I_STICK
|
||||
tile_drop_item = tilemap_types.OBJECT_I_STICK
|
||||
|
||||
# check if the inventory slot is empty
|
||||
if inventory_slot == tilemap_types.EMPTY:
|
||||
|
@ -233,6 +237,7 @@ func tick_handle_food():
|
|||
|
||||
func game_tick() -> void:
|
||||
behavior_tree.game_tick()
|
||||
StepVisualization.add_circle_tileset(board_position, view_distance / 1.2, StepVisualization.CircleType.PLAYER_VIEW)
|
||||
|
||||
tick_handle_temperature(get_current_temperature())
|
||||
tick_handle_food()
|
||||
|
|
|
@ -17,6 +17,12 @@ func _ready() -> void:
|
|||
|
||||
behavior_tree = child as Task
|
||||
|
||||
initialize_blackboard()
|
||||
|
||||
|
||||
func initialize_blackboard() -> void:
|
||||
blackboard["cached_paths"] = {}
|
||||
|
||||
|
||||
func populate_blackboard():
|
||||
blackboard["world"] = game_manager.world
|
||||
|
@ -26,7 +32,10 @@ func populate_blackboard():
|
|||
|
||||
|
||||
func game_tick() -> void:
|
||||
print("game_tick:")
|
||||
populate_blackboard()
|
||||
behavior_tree.internal_run(blackboard)
|
||||
if Task.print_behavior_tree_evaluation:
|
||||
print(" ==> [active state=", blackboard["current_task"], "] [status=", behavior_tree.status, "] [blackboard=", blackboard, "]")
|
||||
|
||||
func find_task_by_name(name: String) -> Task:
|
||||
return behavior_tree.find_task_by_name(name)
|
||||
|
|
|
@ -2,6 +2,8 @@ class_name Task
|
|||
extends Node
|
||||
|
||||
enum {FAILURE = -1, SUCCESS = 1, RUNNING = 0, SUCCESS_STOP = 2}
|
||||
static var print_behavior_tree_evaluation: bool = false
|
||||
#
|
||||
var status: int = FAILURE
|
||||
var status_reason: String = ""
|
||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
|
@ -22,8 +24,10 @@ func internal_run(blackboard: Dictionary) -> void:
|
|||
if running_child != null:
|
||||
extra_string = running_child.name
|
||||
|
||||
if print_behavior_tree_evaluation:
|
||||
print(" -> ", human_readable(extra_string))
|
||||
run(blackboard)
|
||||
if print_behavior_tree_evaluation:
|
||||
print(" <- ", human_readable(extra_string))
|
||||
|
||||
|
||||
|
@ -66,15 +70,7 @@ func get_first_child() -> Task:
|
|||
|
||||
|
||||
func human_readable(addon: String = "") -> String:
|
||||
var clear_status: String = "UNKNOWN"
|
||||
if status == FAILURE:
|
||||
clear_status = "FAILURE"
|
||||
elif status == SUCCESS:
|
||||
clear_status = "SUCCESS"
|
||||
elif status == RUNNING:
|
||||
clear_status = "RUNNING"
|
||||
elif status == SUCCESS_STOP:
|
||||
clear_status = "SUCCESS_STOP"
|
||||
var clear_status: String = clear_status()
|
||||
|
||||
var ret: String = name;
|
||||
if addon != "":
|
||||
|
@ -85,3 +81,53 @@ func human_readable(addon: String = "") -> String:
|
|||
ret += " [" + clear_status + "]"
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
func clear_status() -> String:
|
||||
if status == FAILURE:
|
||||
return "FAILURE"
|
||||
elif status == SUCCESS:
|
||||
return "SUCCESS"
|
||||
elif status == RUNNING:
|
||||
return "RUNNING"
|
||||
elif status == SUCCESS_STOP:
|
||||
return "SUCCESS_STOP"
|
||||
return "UNKNOWN"
|
||||
|
||||
|
||||
# SECTION: utility
|
||||
|
||||
func find_closest_item(blackboard: Dictionary, item_types: Array[Vector2i], memory_key: String, max_distance: int = -1) -> Dictionary:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var result: Dictionary = {"status": FAILURE, "status_reason": "", "closest_item": null}
|
||||
|
||||
var items: Array[Vector2i] = world.tilemap_interactive.get_cells_by_type_collection(
|
||||
item_types, player.board_position, max_distance if max_distance > -1 else player.view_distance)
|
||||
|
||||
if len(items) == 0:
|
||||
result.status_reason = "No items of type " + str(item_types) + " found"
|
||||
return result
|
||||
|
||||
var closest_item: Vector2i = navigation.manhattan_distance_closest(items, player.board_position)
|
||||
player.player_memory[memory_key] = closest_item
|
||||
StepVisualization.add_line_tileset(player.board_position, closest_item, StepVisualization.LineType.SEARCH_SELECTED)
|
||||
if closest_item == tilemap_types.NO_TILE_FOUND:
|
||||
result.status_reason = "No closest item of type " + str(item_types) + " found"
|
||||
return result
|
||||
|
||||
result.status = SUCCESS
|
||||
result.closest_item = closest_item
|
||||
return result
|
||||
|
||||
func find_task_by_name(name: String) -> Task:
|
||||
for c in get_children():
|
||||
if c.name == name:
|
||||
return c
|
||||
if c.get_child_count() > 0:
|
||||
var found: Task = c.find_task_by_name(name)
|
||||
if found != null:
|
||||
return found
|
||||
return null
|
||||
|
|
|
@ -19,6 +19,7 @@ func run(blackboard: Dictionary) -> void:
|
|||
player.walk_along(path)
|
||||
|
||||
if navigation.has_arrived(player.board_position, path):
|
||||
blackboard["cached_paths"] = {}
|
||||
status = SUCCESS
|
||||
status_reason = "already arrived at destination"
|
||||
return
|
||||
|
|
|
@ -19,6 +19,7 @@ func run(blackboard: Dictionary) -> void:
|
|||
player.walk_along(path)
|
||||
|
||||
if navigation.has_arrived(player.board_position, path):
|
||||
blackboard["cached_paths"] = {}
|
||||
status = SUCCESS
|
||||
status_reason = "already arrived at destination"
|
||||
return
|
||||
|
|
|
@ -19,6 +19,7 @@ func run(blackboard: Dictionary) -> void:
|
|||
player.walk_along(path)
|
||||
|
||||
if navigation.has_arrived(player.board_position, path):
|
||||
blackboard["cached_paths"] = {}
|
||||
status = SUCCESS
|
||||
status_reason = "already arrived at destination"
|
||||
return
|
||||
|
|
|
@ -2,17 +2,22 @@ class_name TaskSelector
|
|||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
if get_children().size() == 0:
|
||||
status = FAILURE
|
||||
status_reason = "no children"
|
||||
return
|
||||
|
||||
var running_child: Task = find_running_child()
|
||||
for c in slice_at_child(running_child):
|
||||
run_child(blackboard, c)
|
||||
if c.status == SUCCESS_STOP:
|
||||
c.status = SUCCESS
|
||||
status = SUCCESS
|
||||
status_reason = "stopping at child " + c.name + ", as it returned SUCCESS_STOP"
|
||||
status_reason = "stopping at " + c.name + " (STOP)"
|
||||
return
|
||||
if c.status != FAILURE:
|
||||
status = c.status
|
||||
status_reason = "stopped at child " + c.name
|
||||
status_reason = "stopped at " + c.name
|
||||
return
|
||||
status = FAILURE
|
||||
status_reason = "all children failed"
|
||||
|
|
|
@ -2,17 +2,22 @@ class_name TaskSequence
|
|||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
if get_children().size() == 0:
|
||||
status = FAILURE
|
||||
status_reason = "no children"
|
||||
return
|
||||
|
||||
var running_child: Task = find_running_child()
|
||||
for c in slice_at_child(running_child):
|
||||
run_child(blackboard, c)
|
||||
if c.status == SUCCESS_STOP:
|
||||
c.status = SUCCESS
|
||||
status = SUCCESS
|
||||
status_reason = "stopping at child " + c.name + ", as it returned SUCCESS_STOP"
|
||||
status_reason = "stopping at " + c.name + " (STOP)"
|
||||
return
|
||||
if c.status != SUCCESS:
|
||||
status = c.status
|
||||
status_reason = "stopped at child " + c.name
|
||||
status_reason = "stopped at " + c.name
|
||||
return
|
||||
status = SUCCESS
|
||||
status_reason = "all children succeeded"
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
class_name TaskPlannedExploration
|
||||
extends Task
|
||||
|
||||
var last_goals: Array[Vector2i] = []
|
||||
var current_goal: Vector2i = tilemap_types.NO_TILE_FOUND
|
||||
var closest_distance_to_goal: float = 99999999
|
||||
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
# check if player distance is < 10 to the camp (world, camp_manager, camp) and if he was closer than the view distance to the goal once (closest_distance_to_goal), reset the goal in that case
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, world.camp_manager.camp) < 10 and closest_distance_to_goal < player.view_distance:
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
closest_distance_to_goal = 99999999
|
||||
EventsTracker.track(EventsTracker.Event.EXPLORATION_GOAL_CLOSE_ENOUGH, {"item": tilemap_types.OBJECT_I_TENT})
|
||||
print("Resetting goal, player close to camp and was close to goal once")
|
||||
|
||||
# check if player distance is < 2 to the current goal
|
||||
if current_goal != tilemap_types.NO_TILE_FOUND:
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, current_goal) < 3:
|
||||
EventsTracker.track(EventsTracker.Event.EXPLORATION_GOAL_REACHED, {"goal": current_goal})
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
|
||||
if current_goal == tilemap_types.NO_TILE_FOUND:
|
||||
find_new_goal(world, player, navigation)
|
||||
if current_goal != tilemap_types.NO_TILE_FOUND:
|
||||
EventsTracker.track(EventsTracker.Event.NEW_EXPLORATION_GOAL, {"goal": current_goal})
|
||||
closest_distance_to_goal = 99999999
|
||||
|
||||
if current_goal == tilemap_types.NO_TILE_FOUND:
|
||||
status = Task.FAILURE
|
||||
status_reason = "No goal found"
|
||||
return
|
||||
|
||||
StepVisualization.add_circle_tileset(current_goal, 2, StepVisualization.CircleType.GOAL)
|
||||
StepVisualization.add_line_tileset(player.board_position, current_goal, StepVisualization.LineType.SEARCH_SELECTED)
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "exploration_goal", current_goal)
|
||||
if path.size() == 0:
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
status = Task.FAILURE
|
||||
status_reason = "No path found"
|
||||
return
|
||||
|
||||
blackboard["path"] = path
|
||||
|
||||
status = Task.SUCCESS
|
||||
status_reason = "goal: " + str(current_goal)
|
||||
|
||||
|
||||
func find_new_goal(world: World, player: PlayerManager, navigation: TilemapNavigation) -> void:
|
||||
if last_goals.size() == 0:
|
||||
last_goals.append(world.camp_manager.camp)
|
||||
|
||||
# perform search for a new goal X times, pick the one that is furthest away from the last goal
|
||||
var best_goal: Vector2i = tilemap_types.NO_TILE_FOUND
|
||||
var best_distance: float = 0
|
||||
|
||||
for i in range(3):
|
||||
var goal_consideration: Vector2i = determine_an_interesting_goal(world)
|
||||
if goal_consideration == tilemap_types.NO_TILE_FOUND:
|
||||
continue
|
||||
StepVisualization.add_circle_tileset(goal_consideration, 2, StepVisualization.CircleType.GOAL_CONSIDERATION)
|
||||
StepVisualization.add_line_tileset(player.board_position, goal_consideration, StepVisualization.LineType.SEARCH_BASE)
|
||||
|
||||
var distance: float = TilemapNavigation.manhattan_distance(goal_consideration, navigation.manhattan_distance_closest(last_goals, goal_consideration))
|
||||
if distance > best_distance:
|
||||
best_goal = goal_consideration
|
||||
best_distance = distance
|
||||
|
||||
if best_goal != tilemap_types.NO_TILE_FOUND:
|
||||
last_goals.append(best_goal)
|
||||
current_goal = best_goal
|
||||
|
||||
|
||||
func determine_an_interesting_goal(world: World) -> Vector2i:
|
||||
# starting from the camp position (world.camp_manager.camp),
|
||||
# pick a random direction (360 degrees, random on x and y then normalize),
|
||||
# then step in that direction until the last walkable tile is found
|
||||
# (if not walkable, check every step for the next 10 tiles again and only stop if none of them are walkable)
|
||||
# then, pick a random walkable tile in the area around that last walkable tile,
|
||||
# and check if the player can get there using the navigation system.
|
||||
|
||||
var camp_position: Vector2 = Vector2(world.camp_manager.camp)
|
||||
var direction: Vector2 = Vector2(randf() * 2 - 1, randf() * 2 - 1).normalized()
|
||||
|
||||
var last_walkable: Vector2i = Vector2i(0, 0)
|
||||
var iterations_no_walkable: int = 0
|
||||
|
||||
for i in range(2500):
|
||||
var check_position: Vector2i = camp_position + (direction * i * 2).floor()
|
||||
if not world.is_walkable(check_position):
|
||||
iterations_no_walkable += 1
|
||||
else:
|
||||
iterations_no_walkable = 0
|
||||
last_walkable = check_position
|
||||
|
||||
if iterations_no_walkable > 10:
|
||||
break
|
||||
|
||||
if last_walkable == Vector2i(0, 0):
|
||||
return tilemap_types.NO_TILE_FOUND
|
||||
|
||||
var picked_goal: Vector2i = Vector2i(0, 0)
|
||||
for i in range(10):
|
||||
var check_position: Vector2i = last_walkable + Vector2i(randi_range(-10, 10), randi_range(-10, 10))
|
||||
if world.is_walkable(check_position) and world.tilemap_ground.get_custom_data(check_position, "cost", 999) < 7:
|
||||
picked_goal = check_position
|
||||
break
|
||||
|
||||
return picked_goal
|
|
@ -0,0 +1,14 @@
|
|||
class_name TaskRandomWalking
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var direction: Vector2i = navigation.walking_directions[randi() % navigation.walking_directions.size()]
|
||||
var target: Vector2i = player.board_position + direction
|
||||
|
||||
player.walk_towards(target)
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Walking towards " + str(target)
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCheckFoodBaseThreshold
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if player.food > player.food_base_threshold:
|
||||
status = FAILURE
|
||||
status_reason = "Player food is not base threshold (" + str(player.food) + " > " + str(player.food_critical_threshold) + ")"
|
||||
return
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Player food is base threshold (" + str(player.food) + " <= " + str(player.food_critical_threshold) + ")"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCheckBoatCompleted
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.boat_items.size() >= world.camp_manager.required_boat_parts:
|
||||
status = SUCCESS
|
||||
status_reason = "Boat is completed with " + str(world.camp_manager.boat_items.size()) + " parts"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Boat is not completed, got only " + str(world.camp_manager.boat_items.size()) + " parts"
|
|
@ -0,0 +1,20 @@
|
|||
class_name TaskDeliverBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
EventsTracker.track(EventsTracker.Event.CAMP_BOAT_PART_DELIVERED, {"item": player.inventory_slot})
|
||||
world.camp_manager.boat_items.append(player.inventory_slot)
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
if world.camp_manager.boat_items.size() >= world.camp_manager.required_boat_parts:
|
||||
EventsTracker.track(EventsTracker.Event.CAMP_BOAT_COMPLETE, {"item": tilemap_types.OBJECT_I_BOAT_WITH_ENGINE})
|
||||
world.tilemap_interactive.set_cell(world.camp_manager.boat_build_location, tilemap_types.OBJECT_I_BOAT_WITH_ENGINE)
|
||||
status = SUCCESS
|
||||
status_reason = "Player delivered boat part"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat part to deliver"
|
|
@ -0,0 +1,31 @@
|
|||
class_name TaskFindClosestBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
status = FAILURE
|
||||
status_reason = "Player already has boat part"
|
||||
return
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, "boat_part")
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
var closest_part: Vector2i = result.closest_item
|
||||
blackboard["closest_part"] = closest_part
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat_part", closest_part, player.view_distance * 1.5)
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to closest boat part"
|
||||
status = SUCCESS
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "No path found to closest boat part " + str(closest_part)
|
|
@ -0,0 +1,17 @@
|
|||
class_name TaskGoToBoatLeaveLocation
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var target: Vector2i = world.camp_manager.boat_leave_location
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat_leave", target)
|
||||
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to boat leave location"
|
||||
status = SUCCESS
|
||||
else:
|
||||
status = FAILURE
|
||||
status_reason = "No path found to boat leave location " + str(target)
|
|
@ -0,0 +1,25 @@
|
|||
class_name TaskGoToBoatLocation
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT, "boat_building_location", TileMapLayerAccess.ANY_DISTANCE)
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
# var target: Vector2i = world.camp_manager.boat_build_location
|
||||
var target: Vector2i = result.closest_item
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat", target)
|
||||
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to boat build location"
|
||||
status = SUCCESS
|
||||
else:
|
||||
status = FAILURE
|
||||
status_reason = "No path found to boat build location " + str(target)
|
|
@ -0,0 +1,12 @@
|
|||
class_name TaskWinningSequence
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
world.tilemap_interactive.set_cell(world.camp_manager.boat_leave_location, player.inventory_slot)
|
||||
EventsTracker.track(EventsTracker.Event.PLAYER_USED_ITEM, {"item": player.inventory_slot})
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
|
||||
blackboard["game_state_win"] = true
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCampContainsEnoughSticksToLightCampfire
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.camp_contains_enough_sticks_to_light_campfire():
|
||||
status = SUCCESS
|
||||
status_reason = "Camp contains enough sticks to light campfire"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Camp does not contain enough sticks to light campfire"
|
|
@ -0,0 +1,24 @@
|
|||
class_name TaskCampSleep
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if not world.camp_manager.is_sleep_active and world.camp_manager.is_sundown():
|
||||
world.camp_manager.campfire_light()
|
||||
world.camp_manager.sleep_effect()
|
||||
player.health = player.max_health
|
||||
status = RUNNING
|
||||
status_reason = "Sleeping"
|
||||
return
|
||||
|
||||
if world.camp_manager.is_sleep_active:
|
||||
player.food += 1
|
||||
status = RUNNING
|
||||
status_reason = "Still sleeping"
|
||||
return
|
||||
|
||||
world.camp_manager.campfire_extinguish()
|
||||
status = SUCCESS
|
||||
status_reason = "Slept"
|
|
@ -8,7 +8,7 @@ func run(blackboard: Dictionary) -> void:
|
|||
|
||||
blackboard["location_camp"] = world.camp_manager.camp
|
||||
|
||||
var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, world.camp_manager.camp, 99999999)
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_camp", world.camp_manager.camp, 99999999)
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status = SUCCESS
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
class_name TaskGoCloseToCamp
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
# allow a radius of the player view distance / 1.5 to find a camp
|
||||
var camp: Vector2i = world.camp_manager.camp
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, camp) < player.view_distance / 1.5:
|
||||
blackboard["path"] = []
|
||||
status = SUCCESS
|
||||
status_reason = "Player is close to camp"
|
||||
return
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_camp", camp, 99999999)
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status = FAILURE
|
||||
status_reason = "Found path to camp"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "No path found to camp " + str(camp)
|
|
@ -0,0 +1,16 @@
|
|||
class_name TaskPutInventoryContentInCamp
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if player.inventory_slot != tilemap_types.EMPTY:
|
||||
world.camp_manager.camp_add_item(player.inventory_slot)
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
status = SUCCESS
|
||||
status_reason = "Put inventory content in camp"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player has no inventory content"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskWantsToSleep
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.is_sundown():
|
||||
status = SUCCESS
|
||||
status_reason = "It is sundown " + str(world.camp_manager.time_of_day)
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "It is not sundown " + str(world.camp_manager.time_of_day)
|
|
@ -1,13 +1,27 @@
|
|||
class_name TaskCheckTemperatureCold
|
||||
extends Task
|
||||
|
||||
var last_temperature: float = 0
|
||||
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if player.get_current_temperature() > 0:
|
||||
var current_temperature: int = player.get_current_temperature()
|
||||
|
||||
var temperature_changed: bool = current_temperature != last_temperature
|
||||
var temperature_cold: bool = current_temperature > 0
|
||||
|
||||
if temperature_changed and temperature_cold:
|
||||
EventsTracker.track(EventsTracker.Event.TEMPERATURE_COLD, {"temperature": current_temperature})
|
||||
|
||||
if temperature_changed:
|
||||
last_temperature = current_temperature
|
||||
|
||||
if temperature_cold:
|
||||
status = SUCCESS
|
||||
status_reason = "cold: " + str(player.get_current_temperature())
|
||||
status_reason = "cold: " + str(current_temperature)
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "not cold: " + str(player.get_current_temperature())
|
||||
status_reason = "not cold: " + str(current_temperature)
|
||||
|
|
|
@ -7,7 +7,9 @@ func run(blackboard: Dictionary) -> void:
|
|||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var warm_tiles: Array[Vector2i] = world.tilemap_temperature.get_cells_by_type(
|
||||
tilemap_types.TEMPERATURE_NORMAL, player.board_position, player.view_distance)
|
||||
tilemap_types.TEMPERATURE_NORMAL,
|
||||
player.board_position, player.view_distance,
|
||||
false)
|
||||
if len(warm_tiles) == 0:
|
||||
status = FAILURE
|
||||
status_reason = "No warm tiles found"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsBoat
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT, player.inventory_slot):
|
||||
status = SUCCESS
|
||||
status_reason = "Player has boat"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
status = SUCCESS
|
||||
status_reason = "Player has boat part"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat part"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsStick
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if player.inventory_slot == tilemap_types.OBJECT_I_STICK:
|
||||
status = SUCCESS
|
||||
status_reason = "Player has stick"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have stick"
|
|
@ -0,0 +1,16 @@
|
|||
class_name TaskPickupBoat
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT, "boat_building_location", TileMapLayerAccess.ANY_DISTANCE)
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
player.pick_up_item(result.closest_item)
|
||||
status = SUCCESS
|
||||
status_reason = "Picked up boat"
|
|
@ -0,0 +1,12 @@
|
|||
class_name TaskPickupBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var closest_part: Vector2i = blackboard["closest_part"]
|
||||
|
||||
player.pick_up_item(closest_part)
|
||||
player.player_memory.erase("boat_part")
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Picked up boat part"
|
|
@ -4,9 +4,10 @@ extends Task
|
|||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var tilemap_navigation: TilemapNavigation = blackboard["navigation"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var path: Array[Vector2i] = tilemap_navigation.find_path_allow_neighbors(player.board_position, world.tilemap_mouse_position(), player.view_distance)
|
||||
# var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, world.tilemap_mouse_position(), player.view_distance)
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat_part", world.tilemap_mouse_position(), player.view_distance * 1.4)
|
||||
if len(path) == 0:
|
||||
status = FAILURE
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@ extends Node2D
|
|||
static var game_manager: GameManager
|
||||
static var world: World
|
||||
#
|
||||
enum LineType { SEARCH_BASE, SEARCH_SELECTED }
|
||||
enum LineType { SEARCH_BASE, SEARCH_SELECTED, SEARCH_FAILED }
|
||||
enum CircleType { PLAYER_VIEW, GOAL_CONSIDERATION, GOAL, BOAT_PART }
|
||||
#
|
||||
# Dictionary[Array[Vector2i], LineType] ([from, to], line_type)
|
||||
static var draw_lines: Dictionary = {}
|
||||
# Dictionary[Array[Vector2i], CircleType] ([center, radius], circle_type)
|
||||
static var draw_circles: Dictionary = {}
|
||||
|
||||
|
||||
static func add_line(from: Vector2i, to: Vector2i, line_type: LineType) -> void:
|
||||
|
@ -20,8 +23,19 @@ static func add_line_tileset(from: Vector2i, to: Vector2i, line_type: LineType)
|
|||
draw_lines[[from_tileset, to_tileset]] = line_type
|
||||
|
||||
|
||||
static func add_circle(center: Vector2i, radius: int, circle_type: CircleType) -> void:
|
||||
draw_circles[[center, radius]] = circle_type
|
||||
|
||||
|
||||
static func add_circle_tileset(center: Vector2i, radius: int, circle_type: CircleType) -> void:
|
||||
var center_tileset: Vector2i = world.tilemap_ground.cell_to_local(center)
|
||||
radius *= world.tilemap_ground.tilemap.tile_set.tile_size.x
|
||||
draw_circles[[center_tileset, radius]] = circle_type
|
||||
|
||||
|
||||
func game_tick_start():
|
||||
draw_lines.clear()
|
||||
draw_circles.clear()
|
||||
|
||||
|
||||
func game_tick_end():
|
||||
|
@ -29,7 +43,7 @@ func game_tick_end():
|
|||
|
||||
var label_font = Control.new().get_theme_default_font()
|
||||
|
||||
@export var default_line_color: Color = Color("red")
|
||||
@export var default_color: Color = Color("red")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -37,7 +51,20 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func _draw() -> void:
|
||||
# draw all draw_lines with their labels
|
||||
for key in draw_circles.keys():
|
||||
var center: Vector2i = key[0]
|
||||
var radius: int = key[1]
|
||||
var circle_type: CircleType = draw_circles[key]
|
||||
|
||||
if circle_type == CircleType.PLAYER_VIEW:
|
||||
draw_circle(center, radius, Color("green"), false, 2, true)
|
||||
elif circle_type == CircleType.GOAL_CONSIDERATION:
|
||||
draw_circle(center, radius, Color("yellow"), false, 2, true)
|
||||
elif circle_type == CircleType.GOAL:
|
||||
draw_circle(center, radius, Color("orange"), false, 2, true)
|
||||
elif circle_type == CircleType.BOAT_PART:
|
||||
draw_circle(center, radius, Color(255, 0, 0, 0.5), false, 1, true)
|
||||
|
||||
for key in draw_lines.keys():
|
||||
var from: Vector2i = key[0]
|
||||
var to: Vector2i = key[1]
|
||||
|
@ -47,6 +74,5 @@ func _draw() -> void:
|
|||
draw_line(from, to, Color("blue"), 1)
|
||||
elif line_type == LineType.SEARCH_SELECTED:
|
||||
draw_line(from, to, Color("green"), 2)
|
||||
|
||||
# var center: Vector2 = (from + to) / 2
|
||||
# draw_string(label_font, center, label, 0, -1, 12, text_color)
|
||||
elif line_type == LineType.SEARCH_FAILED:
|
||||
draw_line(from, to, Color(255, 0, 0, 0.1), 1)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
class_name TileMapLayerAccess
|
||||
extends Node
|
||||
|
||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
#
|
||||
var tilemap: TileMapLayer = null
|
||||
var sid: int = 0
|
||||
|
||||
|
@ -11,18 +13,22 @@ func setup() -> void:
|
|||
|
||||
func get_cells_by_type(
|
||||
atlas_coords: Vector2i,
|
||||
center: Vector2i = Vector2i(-1, -1), max_distance: int = 99999999
|
||||
center: Vector2i = Vector2i(-1, -1), max_distance: int = 99999999,
|
||||
record: bool = true
|
||||
) -> Array[Vector2i]:
|
||||
var tiles_with_type: Array[Vector2i] = tilemap.get_used_cells_by_id(sid, atlas_coords)
|
||||
if max_distance < 99999999:
|
||||
var filtered_tiles: Array[Vector2i] = []
|
||||
for tile in tiles_with_type:
|
||||
if TilemapNavigation.manhattan_distance(center, tile, true) <= max_distance:
|
||||
if TilemapNavigation.is_within_radius(center, tile, max_distance, record):
|
||||
filtered_tiles.append(tile)
|
||||
return filtered_tiles
|
||||
return tiles_with_type
|
||||
|
||||
|
||||
const ANY_DISTANCE: int = 99999999
|
||||
|
||||
|
||||
func get_cells_by_type_collection(
|
||||
atlas_coords: Array[Vector2i],
|
||||
center: Vector2i = Vector2i(-1, -1), max_distance: int = 99999999
|
||||
|
@ -33,7 +39,7 @@ func get_cells_by_type_collection(
|
|||
if max_distance < 99999999:
|
||||
var filtered_tiles: Array[Vector2i] = []
|
||||
for tile in tiles_with_type:
|
||||
if TilemapNavigation.manhattan_distance(center, tile, true) <= max_distance:
|
||||
if TilemapNavigation.is_within_radius(center, tile, max_distance, true):
|
||||
filtered_tiles.append(tile)
|
||||
return filtered_tiles
|
||||
return tiles_with_type
|
||||
|
@ -67,6 +73,8 @@ func get_cell(position: Vector2i) -> TileData:
|
|||
|
||||
|
||||
func get_cell_atlas_coords(position: Vector2i) -> Vector2i:
|
||||
if not get_cell(position):
|
||||
return tilemap_types.NO_TILE_FOUND
|
||||
return tilemap.get_cell_atlas_coords(position)
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ const GROUND_WATER_SHALLOW: Vector2i = Vector2i(1, 0)
|
|||
const GROUND_WATER_DEEP: Vector2i = Vector2i(2, 0)
|
||||
const GROUND_SAND: Vector2i = Vector2i(3, 0)
|
||||
const GROUND_DOCK: Vector2i = Vector2i(3, 0)
|
||||
const GROUND_SNOW: Vector2i = Vector2i(4,0)
|
||||
#
|
||||
# objects, sid = 1
|
||||
# NI = not interactive
|
||||
|
@ -20,10 +19,10 @@ const OBJECT_NI_ROCK_1: Vector2i = Vector2i(2, 0)
|
|||
#
|
||||
# I = interactive
|
||||
# boat
|
||||
const OBJECT_I_BOAT_NO_ENIGNE: Vector2i = Vector2i(4, 4)
|
||||
const OBJECT_I_BOAT_WITH_ENGINE: Vector2i = Vector2i(6, 4)
|
||||
const OBJECT_I_BOAT_NO_ENIGNE: Vector2i = Vector2i(0, 4)
|
||||
const OBJECT_I_BOAT_WITH_ENGINE: Vector2i = Vector2i(2, 4)
|
||||
# boat parts
|
||||
const OBJECT_I_BOAT_PART_GENERIC: Vector2i = Vector2i(4584, 5234)
|
||||
const OBJECT_I_BOAT_PART_GENERIC: Vector2i = Vector2i(1, 1)
|
||||
const OBJECT_I_BOAT_PART_ENGINE: Vector2i = Vector2i(0, 1)
|
||||
const OBJECT_I_BOAT_PART_FUEL: Vector2i = Vector2i(1, 1)
|
||||
const OBJECT_I_BOAT_PART_ANCHOR: Vector2i = Vector2i(2, 1)
|
||||
|
@ -53,6 +52,7 @@ const OBJECT_COLLECTION_BOAT_PARTS: Array[Vector2i] = [ # @formatter:off
|
|||
OBJECT_I_BOAT_PART_CHEST, OBJECT_I_BOAT_PART_GEARS, OBJECT_I_BOAT_PART_MEDIKIT,
|
||||
OBJECT_I_BOAT_PART_PADDLE, OBJECT_I_BOAT_PART_GAS_STOVE
|
||||
] # @formatter:on
|
||||
const OBJECT_COLLECTION_BOAT: Array[Vector2i] = [OBJECT_I_BOAT_NO_ENIGNE, OBJECT_I_BOAT_WITH_ENGINE]
|
||||
#
|
||||
# temperature, sid = 2
|
||||
const TEMPERATURE_NORMAL: Vector2i = Vector2i(2, 0)
|
||||
|
@ -83,3 +83,6 @@ func player_sprite_from_direction(direction: Vector2i) -> Vector2i:
|
|||
if direction == Vector2i(1, 0):
|
||||
return PLAYER_RIGHT
|
||||
return PLAYER_DOWN
|
||||
|
||||
func is_part_of_collection(collection: Array[Vector2i], item: Vector2i) -> bool:
|
||||
return collection.find(item) != -1
|
||||
|
|
|
@ -32,15 +32,18 @@ func game_tick_end() -> void:
|
|||
world.tilemap_nav_vis.set_cell(chosen_path[chosen_path.size() - 1], tilemap_types.NAVIGATION_TARGET)
|
||||
|
||||
|
||||
func is_within_radius(position: Vector2i, center: Vector2i, radius: int, record: bool = false) -> bool:
|
||||
return TilemapNavigation.manhattan_distance(position, center, record) <= radius
|
||||
|
||||
|
||||
static func manhattan_distance(a: Vector2i, b: Vector2i, record: bool = false) -> int:
|
||||
var dist: int = abs(a.x - b.x) + abs(a.y - b.y)
|
||||
static func is_within_radius(position: Vector2i, center: Vector2i, radius: int, record: bool = false) -> bool:
|
||||
var is_within: bool = TilemapNavigation.manhattan_distance(position, center) <= radius
|
||||
if record:
|
||||
StepVisualization.add_line_tileset(a, b, StepVisualization.LineType.SEARCH_BASE)
|
||||
return dist
|
||||
if is_within:
|
||||
StepVisualization.add_line_tileset(center, position, StepVisualization.LineType.SEARCH_BASE)
|
||||
else:
|
||||
StepVisualization.add_line_tileset(center, position, StepVisualization.LineType.SEARCH_FAILED)
|
||||
return is_within
|
||||
|
||||
|
||||
static func manhattan_distance(a: Vector2i, b: Vector2i) -> int:
|
||||
return abs(a.x - b.x) + abs(a.y - b.y)
|
||||
|
||||
|
||||
func manhattan_distance_closest(options: Array[Vector2i], target: Vector2i) -> Vector2i:
|
||||
|
@ -91,6 +94,46 @@ func has_arrived(position: Vector2i, path: Array[Vector2i]) -> bool:
|
|||
return path.size() > 0 and path[path.size() - 1] == position
|
||||
|
||||
|
||||
func path_still_valid(require_on_path: Vector2i, require_close_end: Vector2i, path: Array[Vector2i]) -> bool:
|
||||
# check if:
|
||||
# - player is on the path
|
||||
# - target is close to the last position in the path (<= 1 step away)
|
||||
# - all positions are still walkable
|
||||
|
||||
if not require_on_path == tilemap_types.NO_TILE_FOUND and not path.has(require_on_path):
|
||||
return false
|
||||
|
||||
if not require_close_end == tilemap_types.NO_TILE_FOUND and (path.size() == 0 or not is_within_radius(require_close_end, path[path.size() - 1], 1)):
|
||||
return false
|
||||
|
||||
for pos in path:
|
||||
if not world.is_walkable(pos):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func cached_path_allow_neighbors(blackboard: Dictionary, path_key: String, target: Vector2i, max_radius: int = -1) -> Array[Vector2i]:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if blackboard["cached_paths"].has(path_key):
|
||||
# clear ALL other that are not the current path
|
||||
for key in blackboard["cached_paths"].keys():
|
||||
if key != path_key:
|
||||
blackboard["cached_paths"].erase(key)
|
||||
# check if the path is still valid
|
||||
if path_still_valid(player.board_position, target, blackboard["cached_paths"][path_key]):
|
||||
return blackboard["cached_paths"][path_key]
|
||||
else:
|
||||
print("Cached path is invalid, recalculating for ", target, " ", path_key)
|
||||
blackboard["cached_paths"].erase(path_key)
|
||||
|
||||
StepVisualization.add_line_tileset(player.board_position, target, StepVisualization.LineType.SEARCH_SELECTED)
|
||||
var path: Array[Vector2i] = find_path_allow_neighbors(player.board_position, target, max_radius)
|
||||
if path.size() > 0:
|
||||
blackboard["cached_paths"][path_key] = path
|
||||
return path
|
||||
|
||||
|
||||
func find_path(start_position: Vector2i, end_position: Vector2i, max_radius: int = -1) -> Array[Vector2i]:
|
||||
var path: Array[Vector2i] = _find_path_internal(start_position, end_position, max_radius)
|
||||
if path.size() > 0:
|
||||
|
|
|
@ -56,6 +56,11 @@ func tilemap_mouse_position() -> Vector2i:
|
|||
|
||||
|
||||
func find_item_drop_location(center_pos: Vector2i) -> Vector2i:
|
||||
for x in range(center_pos.x - 1, center_pos.x + 1):
|
||||
for y in range(center_pos.y - 1, center_pos.y + 1):
|
||||
var check_pos: Vector2i = Vector2i(x, y)
|
||||
if not tilemap_interactive.get_cell(check_pos) and is_walkable(check_pos):
|
||||
return check_pos
|
||||
for x in range(center_pos.x - 2, center_pos.x + 2):
|
||||
for y in range(center_pos.y - 2, center_pos.y + 2):
|
||||
var check_pos: Vector2i = Vector2i(x, y)
|
||||
|
@ -74,6 +79,7 @@ func is_walkable(position: Vector2i) -> bool:
|
|||
|
||||
func game_tick_start() -> void:
|
||||
step_visualizer.game_tick_start()
|
||||
camp_manager.game_tick_start()
|
||||
|
||||
# refill empty bushes
|
||||
var empty_bushes: Array[Vector2i] = tilemap_interactive.get_cells_by_type(tilemap_types.OBJECT_I_EMPTY_BUSH)
|
||||
|
@ -87,6 +93,12 @@ func game_tick_start() -> void:
|
|||
if randf() < 0.01:
|
||||
tilemap_interactive.set_cell(tree, tilemap_types.OBJECT_I_TREE_FULL)
|
||||
|
||||
# mark all boat parts on the map
|
||||
var boat_parts: Array[Vector2i] = tilemap_interactive.get_cells_by_type_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS)
|
||||
for part in boat_parts:
|
||||
StepVisualization.add_circle_tileset(part, 1, StepVisualization.CircleType.BOAT_PART)
|
||||
|
||||
|
||||
func game_tick_end() -> void:
|
||||
step_visualizer.game_tick_end()
|
||||
camp_manager.game_tick_end()
|
||||
|
|
|
@ -6,9 +6,17 @@ var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
|||
var game_manager: GameManager = null
|
||||
var tilemap_interactive: TileMapLayerAccess = null
|
||||
#
|
||||
var items: Array[Vector2i] = []
|
||||
var camp: Vector2i = Vector2i(0, 0)
|
||||
var campfire: Vector2i = Vector2i(0, 0)
|
||||
var camp_items: Array[Vector2i] = []
|
||||
var boat_items: Array[Vector2i] = []
|
||||
var camp: Vector2i = tilemap_types.EMPTY
|
||||
var campfire: Vector2i = tilemap_types.EMPTY
|
||||
var boat_build_location: Vector2i = tilemap_types.EMPTY
|
||||
var boat_leave_location: Vector2i = tilemap_types.EMPTY
|
||||
#
|
||||
var time_of_day: int = 0
|
||||
var day_length: int = 1000
|
||||
|
||||
@export var required_boat_parts: int = 8
|
||||
|
||||
|
||||
func setup() -> void:
|
||||
|
@ -18,30 +26,62 @@ func setup() -> void:
|
|||
camp = camp_locations[0]
|
||||
else:
|
||||
push_error("No camp location found on tilemap")
|
||||
|
||||
var firepit_locations: Array[Vector2i] = tilemap_interactive.get_cells_by_type_collection(tilemap_types.OBJECT_COLLECTION_FIREPIT)
|
||||
if len(firepit_locations) > 0:
|
||||
campfire = firepit_locations[0]
|
||||
else:
|
||||
push_error("No firepit location found on tilemap")
|
||||
print("CampManager: camp=", camp, " campfire=", campfire)
|
||||
|
||||
var boat_build_locations: Array[Vector2i] = tilemap_interactive.get_cells_by_type(tilemap_types.OBJECT_I_BOAT_NO_ENIGNE)
|
||||
if len(boat_build_locations) > 0:
|
||||
boat_build_location = boat_build_locations[0]
|
||||
else:
|
||||
push_error("No boat build location found on tilemap")
|
||||
|
||||
var boat_leave_locations: Array[Vector2i] = tilemap_interactive.get_cells_by_type(tilemap_types.OBJECT_I_BOAT_WITH_ENGINE)
|
||||
if len(boat_leave_locations) > 0:
|
||||
boat_leave_location = boat_leave_locations[0]
|
||||
tilemap_interactive.set_cell(boat_leave_location, tilemap_types.EMPTY)
|
||||
else:
|
||||
push_error("No boat leave location found on tilemap")
|
||||
|
||||
print("CampManager: camp=", camp, " campfire=", campfire, " boat_build_location=", boat_build_location, " boat_leave_location=", boat_leave_location)
|
||||
|
||||
tilemap_interactive.set_cell(campfire, tilemap_types.OBJECT_I_FIREPIT_OFF)
|
||||
|
||||
|
||||
func game_tick_start() -> void:
|
||||
time_of_day += 1
|
||||
|
||||
|
||||
func game_tick_end() -> void:
|
||||
if time_of_day == day_length:
|
||||
EventsTracker.track(EventsTracker.Event.TIME_SUNDOWN)
|
||||
|
||||
|
||||
func is_sundown() -> bool:
|
||||
return time_of_day >= day_length
|
||||
|
||||
|
||||
func camp_contains_enough_sticks_to_light_campfire() -> bool:
|
||||
return camp_item_count(tilemap_types.OBJECT_I_STICK) >= 2
|
||||
|
||||
|
||||
func camp_contains_item(item: Vector2i) -> bool:
|
||||
return items.find(item) != -1
|
||||
return camp_items.find(item) != -1
|
||||
|
||||
|
||||
func camp_contains_item_collection(item: Array[Vector2i]) -> bool:
|
||||
for i in item:
|
||||
if items.find(i) == -1:
|
||||
if camp_items.find(i) == -1:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func camp_item_count(item: Vector2i) -> int:
|
||||
var count: int = 0
|
||||
for i in items:
|
||||
for i in camp_items:
|
||||
if i == item:
|
||||
count += 1
|
||||
return count
|
||||
|
@ -49,7 +89,7 @@ func camp_item_count(item: Vector2i) -> int:
|
|||
|
||||
func camp_item_collection_count(item: Array[Vector2i]) -> int:
|
||||
var count: int = 0
|
||||
for i in items:
|
||||
for i in camp_items:
|
||||
if item.find(i) != -1:
|
||||
count += 1
|
||||
return count
|
||||
|
@ -62,9 +102,9 @@ func camp_take_item(item: Vector2i, count: int = 1) -> bool:
|
|||
return false
|
||||
|
||||
var taken: int = 0
|
||||
for i in range(items.size()):
|
||||
if items[i] == item:
|
||||
items.remove_at(i)
|
||||
for i in range(camp_items.size() - 1, -1, -1):
|
||||
if camp_items[i] == item:
|
||||
camp_items.remove_at(i)
|
||||
taken += 1
|
||||
if taken == count:
|
||||
break
|
||||
|
@ -74,7 +114,7 @@ func camp_take_item(item: Vector2i, count: int = 1) -> bool:
|
|||
|
||||
|
||||
func camp_add_item(item: Vector2i) -> void:
|
||||
items.append(item)
|
||||
camp_items.append(item)
|
||||
EventsTracker.track(EventsTracker.Event.CAMP_ADDED_ITEM, {"item": item, "count": 1, "new_count": camp_item_count(item)})
|
||||
|
||||
|
||||
|
@ -93,11 +133,52 @@ func campfire_extinguish() -> void:
|
|||
tilemap_interactive.set_cell(campfire, tilemap_types.OBJECT_I_FIREPIT_OFF)
|
||||
EventsTracker.track(EventsTracker.Event.CAMPFIRE_EXTINGUISHED)
|
||||
|
||||
var is_sleep_active: bool = false
|
||||
|
||||
|
||||
func sleep_effect() -> void:
|
||||
if is_sleep_active:
|
||||
return
|
||||
is_sleep_active = true
|
||||
EventsTracker.track(EventsTracker.Event.SLEEP)
|
||||
|
||||
game_manager.camera.go_to_zooming(game_manager.world.tilemap_player.cell_to_local(camp), 3)
|
||||
var tween_in: Tween = game_manager.world.get_tree().create_tween()
|
||||
tween_in.tween_method(game_manager.camera.set_vignette_intensity, 0.0, 1.0, 2.0).set_delay(0.5)
|
||||
var tween_out: Tween = game_manager.world.get_tree().create_tween()
|
||||
tween_out.tween_method(game_manager.camera.set_vignette_intensity, 1.0, 0.0, 2.0).set_delay(4.0)
|
||||
|
||||
await game_manager.world.get_tree().create_timer(6.0).timeout
|
||||
|
||||
print("Sleep effect done")
|
||||
is_sleep_active = false
|
||||
time_of_day = 0
|
||||
|
||||
|
||||
func populate_camp_visualization(boat_ui: HBoxContainer, camp_ui: HBoxContainer) -> void:
|
||||
for child in boat_ui.get_children():
|
||||
if child.name != "HeightLabel":
|
||||
boat_ui.remove_child(child)
|
||||
|
||||
for boat_part in boat_items:
|
||||
var texture: TextureRect = create_item_texture(boat_part)
|
||||
boat_ui.add_child(texture)
|
||||
|
||||
for child in camp_ui.get_children():
|
||||
if child.name != "HeightLabel":
|
||||
camp_ui.remove_child(child)
|
||||
|
||||
for boat_part in camp_items:
|
||||
var texture: TextureRect = create_item_texture(boat_part)
|
||||
camp_ui.add_child(texture)
|
||||
|
||||
|
||||
func create_item_texture(item: Vector2i) -> TextureRect:
|
||||
var item_texture: Texture = game_manager.world.tilemap_interactive.get_cell_texture(item)
|
||||
if item_texture:
|
||||
var item_texture_rect: TextureRect = TextureRect.new()
|
||||
item_texture_rect.texture = item_texture
|
||||
item_texture_rect.set_expand_mode(TextureRect.EXPAND_FIT_WIDTH)
|
||||
item_texture_rect.set_stretch_mode(TextureRect.STRETCH_KEEP_ASPECT_CENTERED)
|
||||
return item_texture_rect
|
||||
return null
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
class_name BehaviorTreeVisualizer
|
||||
extends Window
|
||||
|
||||
const D_TREE_NODE: PackedScene = preload("res://scripts/visualization/d_tree_node.tscn")
|
||||
@onready var graph_edit: GraphEdit = %GraphEdit
|
||||
|
||||
#
|
||||
var behavior_tree: BehaviorTree
|
||||
#
|
||||
var x_spacing: int = 430
|
||||
var y_spacing: int = 100
|
||||
#
|
||||
var all_nodes: Array[DTreeNode] = []
|
||||
# Dictionary[Task, DTreeNode]
|
||||
var task_to_node: Dictionary = {}
|
||||
var parent_nodes: Dictionary = {}
|
||||
#
|
||||
var current_lowest_node_pos: Vector2 = Vector2(0, 0)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("toggle_graph_edit"):
|
||||
if is_visible():
|
||||
hide()
|
||||
else:
|
||||
show()
|
||||
|
||||
func build_tree() -> void:
|
||||
if not behavior_tree:
|
||||
push_error("No behavior tree set.")
|
||||
return
|
||||
|
||||
# reset current visualization
|
||||
graph_edit.clear_connections()
|
||||
for node in all_nodes:
|
||||
node.queue_free()
|
||||
all_nodes.clear()
|
||||
|
||||
var root_task: Task = behavior_tree.behavior_tree
|
||||
if not root_task:
|
||||
push_error("Root behavior tree node is null.")
|
||||
return
|
||||
|
||||
current_lowest_node_pos = Vector2(0, 0)
|
||||
build_tree_from_task(root_task, 0)
|
||||
|
||||
|
||||
func build_tree_from_task(task: Task, depth: int) -> DTreeNode:
|
||||
var child_nodes: Array[DTreeNode] = []
|
||||
var child_node_positions: Array[Vector2] = []
|
||||
|
||||
for child in task.get_children():
|
||||
var child_node: DTreeNode = build_tree_from_task(child, depth + 1)
|
||||
child_nodes.append(child_node)
|
||||
child_node_positions.append(child_node.position_offset)
|
||||
|
||||
var current_node: DTreeNode = D_TREE_NODE.instantiate()
|
||||
graph_edit.add_child(current_node)
|
||||
task_to_node[task] = current_node
|
||||
current_node.name = task.get_name() + str(randf())
|
||||
current_node.title = human_readable_task_name(task.get_name())
|
||||
current_node.add_label("status", true, true)
|
||||
all_nodes.append(current_node)
|
||||
|
||||
for child in child_nodes:
|
||||
graph_edit.connect_node(current_node.name, 0, child.name, 0)
|
||||
parent_nodes[child] = current_node
|
||||
|
||||
if child_node_positions.size() > 0:
|
||||
var average_position: Vector2 = Vector2(0, 0)
|
||||
for pos in child_node_positions:
|
||||
average_position += pos
|
||||
average_position /= child_node_positions.size()
|
||||
|
||||
current_node.position_offset = average_position - Vector2(x_spacing, 0)
|
||||
# print("as parent: ", current_node.name, " ", current_node.position_offset, " ", depth, " ", child_node_positions)
|
||||
|
||||
else:
|
||||
current_node.position_offset = Vector2(current_lowest_node_pos)
|
||||
current_node.position_offset.x += depth * x_spacing
|
||||
current_lowest_node_pos.y += y_spacing
|
||||
# print("as leaf: ", current_node.name, " ", current_node.position_offset, " ", depth)
|
||||
pass
|
||||
|
||||
return current_node
|
||||
|
||||
|
||||
func update_task_statuses(blackboard: Dictionary) -> void:
|
||||
for t in task_to_node.keys():
|
||||
var task: Task = t as Task
|
||||
var node: DTreeNode = task_to_node[task]
|
||||
var status: int = task.status
|
||||
var clear_status: String = task.clear_status()
|
||||
var status_reason: String = task.status_reason
|
||||
|
||||
if status_reason != "":
|
||||
node.set_label_text(0, status_reason)
|
||||
else:
|
||||
node.set_label_text(0, clear_status)
|
||||
|
||||
node.set_body_color(node.color_normal)
|
||||
if status == Task.RUNNING or status == Task.SUCCESS or status == Task.SUCCESS_STOP:
|
||||
node.set_body_color(node.color_success)
|
||||
|
||||
if blackboard.has("current_task"):
|
||||
var selected_node = task_to_node[blackboard["current_task"]]
|
||||
if selected_node:
|
||||
center_view_on_position(selected_node.position_offset)
|
||||
selected_node.set_body_color(selected_node.color_executed)
|
||||
while parent_nodes.has(selected_node):
|
||||
selected_node = parent_nodes[selected_node]
|
||||
selected_node.set_body_color(selected_node.color_checked)
|
||||
|
||||
|
||||
func center_view_on_position(target_position: Vector2) -> void:
|
||||
var graph_edit_size: Vector2 = graph_edit.size
|
||||
var zoom: float = graph_edit.zoom
|
||||
var offset_x: float = target_position.x * zoom - graph_edit_size.x / 2
|
||||
var offset_y: float = target_position.y * zoom - graph_edit_size.y / 2
|
||||
graph_edit.scroll_offset = Vector2(offset_x, offset_y)
|
||||
|
||||
|
||||
func human_readable_task_name(input: String) -> String:
|
||||
var prefixes: Dictionary = {"sl_": "Selector: ", "sq_": "Sequence: ", "Task": ""}
|
||||
var selected_prefix: String = ""
|
||||
|
||||
for prefix in prefixes.keys():
|
||||
if input.begins_with(prefix):
|
||||
selected_prefix = prefix
|
||||
input = input.substr(prefix.length())
|
||||
break
|
||||
|
||||
var words: Array[Variant] = []
|
||||
var current_word: String = ""
|
||||
|
||||
for i in range(input.length()):
|
||||
var character: String = input[i]
|
||||
if character.to_upper() == character and current_word.length() > 0:
|
||||
words.append(current_word)
|
||||
current_word = "" + character.to_lower()
|
||||
elif character == "_":
|
||||
if current_word.length() > 0:
|
||||
words.append(current_word)
|
||||
current_word = ""
|
||||
else:
|
||||
current_word += character.to_lower()
|
||||
|
||||
if current_word.length() > 0:
|
||||
words.append(current_word)
|
||||
|
||||
var result: String = " ".join(words)
|
||||
if selected_prefix in prefixes and prefixes[selected_prefix] != "":
|
||||
result = prefixes[selected_prefix] + result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
hide()
|
|
@ -0,0 +1,45 @@
|
|||
class_name DTreeNode
|
||||
extends GraphNode
|
||||
|
||||
var left_slots: Array[String] = []
|
||||
var right_slots: Array[String] = []
|
||||
var color_normal: StyleBoxFlat = StyleBoxFlat.new()
|
||||
var color_success: StyleBoxFlat = StyleBoxFlat.new()
|
||||
var color_executed: StyleBoxFlat = StyleBoxFlat.new()
|
||||
var color_checked: StyleBoxFlat = StyleBoxFlat.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
color_normal.bg_color = Color(1.0, 1.0, 1.0)
|
||||
color_success.bg_color = Color(0.25490198, 0.78431374, 0.9529412)
|
||||
color_executed.bg_color = Color(0.5058824, 0.9529412, 0.30588236)
|
||||
color_checked.bg_color = Color(0.6509804, 0.9254902, 0.5372549)
|
||||
|
||||
|
||||
func add_label(label_text: String, left: bool, right: bool) -> Vector3i:
|
||||
var new_label: Label = Label.new()
|
||||
new_label.text = label_text
|
||||
new_label.add_theme_color_override("font_color", Color(0, 0, 0, 1))
|
||||
|
||||
self.add_child(new_label)
|
||||
|
||||
var child_index: int = self.get_child_count() - 1
|
||||
if left:
|
||||
self.set_slot_enabled_left(child_index, true)
|
||||
self.set_slot_color_left(child_index, Color(0.9, 0.9, 0.9, 1))
|
||||
left_slots.append(label_text)
|
||||
if right:
|
||||
self.set_slot_enabled_right(child_index, true)
|
||||
self.set_slot_color_right(child_index, Color(0.9, 0.9, 0.9, 1))
|
||||
right_slots.append(label_text)
|
||||
|
||||
# the port index is counted separately from the left and right slots
|
||||
return Vector3i(child_index, left_slots.size() - 1, right_slots.size() - 1)
|
||||
|
||||
|
||||
func set_body_color(color: StyleBoxFlat) -> void:
|
||||
self.add_theme_stylebox_override("panel", color)
|
||||
|
||||
func set_label_text(label_index: int, text: String) -> void:
|
||||
var label: Label = self.get_child(label_index) as Label
|
||||
label.text = text
|
|
@ -0,0 +1,18 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://bmv75j2e8xc6n"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/visualization/d_tree_node.gd" id="1_o2ffa"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k54lu"]
|
||||
bg_color = Color(0.95158, 0.95158, 0.95158, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dmqj2"]
|
||||
bg_color = Color(0.6, 0.39, 0.39, 1)
|
||||
|
||||
[node name="DTreeNode" type="GraphNode"]
|
||||
offset_right = 41.0
|
||||
offset_bottom = 23.0
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_k54lu")
|
||||
theme_override_styles/titlebar = SubResource("StyleBoxFlat_dmqj2")
|
||||
title = "TITEL"
|
||||
script = ExtResource("1_o2ffa")
|
|
@ -0,0 +1,60 @@
|
|||
class_name InfoPanel
|
||||
extends VBoxContainer
|
||||
|
||||
var values: Dictionary = {}
|
||||
var last_values: Dictionary = {}
|
||||
var value_order: Array = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
hide()
|
||||
|
||||
|
||||
func to_str(value) -> String:
|
||||
if value is float:
|
||||
return str(round(value * 100) / 100)
|
||||
elif value is Vector2:
|
||||
return "(" + to_str(value.x) + ", " + to_str(value.y) + ")"
|
||||
elif value is Vector3:
|
||||
return "(" + to_str(value.x) + ", " + to_str(value.y) + ", " + to_str(value.z) + ")"
|
||||
elif value is Vector4:
|
||||
return "(" + to_str(value.x) + ", " + to_str(value.y) + ", " + to_str(value.z) + ", " + to_str(value.w) + ")"
|
||||
return str(value)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("debug_vis_2"):
|
||||
if is_visible():
|
||||
hide()
|
||||
else:
|
||||
show()
|
||||
|
||||
for child in self.get_children():
|
||||
if child is Label:
|
||||
child.queue_free()
|
||||
|
||||
# 1. Update and Track Order of Values
|
||||
for value in values.keys():
|
||||
if value in value_order:
|
||||
# Move to the end (most recent)
|
||||
value_order.erase(value)
|
||||
value_order.append(value)
|
||||
|
||||
# 2. Display Current Values (Most Recent First)
|
||||
for value in value_order:
|
||||
if values.has(value):
|
||||
var new_label: Label = Label.new()
|
||||
new_label.text = value + ": " + to_str(values[value])
|
||||
new_label.add_theme_color_override("font_color", Color(0, 0, 0, 1))
|
||||
self.add_child(new_label)
|
||||
last_values[value] = values[value]
|
||||
|
||||
# 3. Display Old Values
|
||||
for value in last_values.keys():
|
||||
if not values.has(value):
|
||||
var new_label: Label = Label.new()
|
||||
new_label.text = value + ": " + to_str(last_values[value])
|
||||
new_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.5, 1))
|
||||
self.add_child(new_label)
|
||||
|
||||
values.clear()
|
Loading…
Reference in New Issue