Compare commits

..

2 Commits
main ... main

Author SHA1 Message Date
Luca 7cb2330ee9 Erste DecisionTree Struktur erstellt 2025-01-04 13:49:25 +01:00
Luca 3f6c10703c Erste DecisionTree Struktur erstellt 2025-01-04 13:48:13 +01:00
135 changed files with 975 additions and 3645 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
project/.godot
.idea
.vscode

View File

@ -1,34 +1,4 @@
## Todo
- Sprites (Dome)
- camp (multiple textures)
- boat
- see DC
- Design a tilemap for the game (Dome)
- Navigation v3 (Yan)
- Player: function to walk up to a tile, not onto the tile (trees, etc.)
- Visualization: Current navigation path
- Interactive and Non-Interactive Items
- Add all the items needed to build the game (bushes, trees, etc.)
- Add logic regarding the objects, like taking branches, etc.
- Camp, chest (inventory slots, array of items, Interactions)
- Implement Behaviours
- Implement all kinds of Behaviours, see document
- UI, Visualization, make the simulation understandable (Luca, Colin)
- GraphEdit
- GraphEdit toggle (key)
- Inventory
- Player Stats
- Temperature layer toggle (key)
- etc.
## Done
- Sprites (Dome)
- Ground
- Berry bush (filled, empty)
- Tree (filled, empty)
- Ship parts
- Sprites (dome)
- Initialize Tilemap (Yan)
- Script --> World (manages access to tilemap)
- Player is on tilemap
@ -37,24 +7,25 @@
- Obstacles (Stone, ...)
- Pickup (Berries, Trees, Boat parts) --> custom data (Type of collectable "tree", "berry", "boat")
- Temperature layer (normal and different levels of coldness, transparent solid color)
- Decision Tree (Classes, etc.) (Yan)
- Reference Food Gatherer for implementation
- Child of player, serves as controller
- Script needs access to the scene, player and other objects/data
- Navigation
- Use tilemap layers to compute route
- Support obstacles
- Result must be an array of tile coordinates, length (array length) and the total cost (sum of weights)
- Player
- Design a tilemap for the game
- Player (Dome)
- Stats (see document)
- As a layer in the tilemap
- Implemented in the PlayerManager
- Inventory
- with one slot
- can pick up items from tilemap (Pickup)
- can drop items back onto tilemap (Pickup)
- Navigation v2 (Yan)
- Support walkable attribute on interactive/non-interactive tilemap items (is_walkable)
- fix pathfinding for unreachable cells
- add search radius (max distance) before canceling
- improve performance (or rather, check performance first)
- Navigation (Dome?)
- Use tilemap layers to compute route
- Support obstacles
- Result must be an array of tile coordinates, length (array length) and the total cost (sum of weights)
- Decision Tree (Classes, etc.) (Luca, Cool in)
- Reference Food Gatherer for implementation
- Child of player, serves as controller
- Script needs access to the scene, player and other objects/data
- Implement Behaviours
- Implement all kinds of Behaviours, see document
- Visualization, make the simulation understandable
- GraphEdit
- Distances
- Current navigation path
- ...

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

View File

@ -1,34 +0,0 @@
[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.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,34 +0,0 @@
[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.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1,34 +0,0 @@
[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

View File

@ -1,17 +0,0 @@
shader_type canvas_item;
uniform float inner_radius = 0.1;
uniform float outer_radius = 1;
uniform float vignette_strength = 1.0;
uniform float dither_strength = 0.03;
uniform vec4 vignette_color: source_color;
void fragment() {
float dist = distance(UV, vec2(0.5));
float vignette = smoothstep(inner_radius, outer_radius, dist) * vignette_strength;
float dither = fract(sin(dot(UV, vec2(12.9898, 78.233))) * 43758.5453123) * dither_strength;
COLOR = vec4(vignette_color.rgb, vignette + dither);
}

View File

@ -1,11 +0,0 @@
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://ckg3be082ny3h"]
[ext_resource type="Shader" path="res://assets/shader/shader_vignette.gdshader" id="1_6o5k2"]
[resource]
shader = ExtResource("1_6o5k2")
shader_parameter/inner_radius = 0.1
shader_parameter/outer_radius = 0.725
shader_parameter/vignette_strength = 0.0
shader_parameter/dither_strength = 0.03
shader_parameter/vignette_color = Color(0, 0, 0, 1)

View File

@ -1 +1,48 @@
{"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}
{
"height": 320,
"layers": [
{
"cels": [
{
"image": "tilemaps\\tilemap_ground.png",
"frame": 0
}
],
"name": "ground"
},
{
"cels": [
{
"image": "tilemaps\\tilemap_objects.png",
"frame": 0
}
],
"name": "objects"
},
{
"cels": [
{
"image": "tilemaps\\tilemap_temperature.png",
"frame": 0
}
],
"name": "temperature"
},
{
"cels": [
{
"image": "tilemaps\\tilemap_player.png",
"frame": 0
}
],
"name": "player"
}
],
"width": 320,
"frames": [
{
"duration": 0.1
}
],
"filename": "tilemaps.aseprite"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 470 B

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://diwoxcyj13q7v"
uid="uid://dy0gpc2vgr3o5"
path="res://.godot/imported/tilemap_ground.png-cb404afe66e487b3999901e2d621baa7.ctex"
metadata={
"vram_texture": false
@ -31,4 +31,4 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 313 B

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dkvyu6a2bqans"
uid="uid://cvb8hqljk0rv3"
path="res://.godot/imported/tilemap_objects.png-36dc04b2c5c4ea8db297745921fe10e8.ctex"
metadata={
"vram_texture": false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 132 B

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bwkvdumh22cmh"
uid="uid://bam5su5j62mof"
path="res://.godot/imported/tilemap_player.png-ea1fa2aedf5bb220961e9080aa573e32.ctex"
metadata={
"vram_texture": false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 98 B

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dtoy4tdohey8t"
uid="uid://d1sdhqjtrt1ng"
path="res://.godot/imported/tilemap_temperature.png-df4acfda23c8781105475512aa320086.ctex"
metadata={
"vram_texture": false

View File

@ -1,125 +1,41 @@
[gd_resource type="TileSet" load_steps=9 format=3 uid="uid://bi836ygcmyvhb"]
[ext_resource type="Texture2D" uid="uid://diwoxcyj13q7v" path="res://assets/tilemap/tilemaps/tilemap_ground.png" id="1_ukrsa"]
[ext_resource type="Texture2D" uid="uid://dkvyu6a2bqans" path="res://assets/tilemap/tilemaps/tilemap_objects.png" id="2_15xge"]
[ext_resource type="Texture2D" uid="uid://dtoy4tdohey8t" path="res://assets/tilemap/tilemaps/tilemap_temperature.png" id="3_xap0v"]
[ext_resource type="Texture2D" uid="uid://bwkvdumh22cmh" path="res://assets/tilemap/tilemaps/tilemap_player.png" id="4_f38wc"]
[ext_resource type="Texture2D" uid="uid://dy0gpc2vgr3o5" path="res://assets/tilemap/tilemaps/tilemap_ground.png" id="1_ukrsa"]
[ext_resource type="Texture2D" uid="uid://cvb8hqljk0rv3" path="res://assets/tilemap/tilemaps/tilemap_objects.png" id="2_o4fdg"]
[ext_resource type="Texture2D" uid="uid://d1sdhqjtrt1ng" path="res://assets/tilemap/tilemaps/tilemap_temperature.png" id="3_xap0v"]
[ext_resource type="Texture2D" uid="uid://bam5su5j62mof" path="res://assets/tilemap/tilemaps/tilemap_player.png" id="4_f38wc"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_114re"]
texture = ExtResource("1_ukrsa")
1:0/0 = 0
1:0/0/custom_data_2 = 1
2:0/0 = 0
2:0/0/custom_data_2 = 1
3:0/next_alternative_id = 9
3:0/0 = 0
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 = 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 = 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
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")
0:0/0 = 0
1:0/0 = 0
2:0/0 = 0
0:1/0 = 0
0:1/0/custom_data_0 = true
0:2/0 = 0
0:2/0/custom_data_0 = true
0:4/size_in_atlas = Vector2i(2, 1)
0:4/0 = 0
0:5/0 = 0
0:7/0 = 0
3:0/0 = 0
1:1/0 = 0
1:1/0/custom_data_0 = true
2:1/0 = 0
2:1/0/custom_data_0 = true
3:1/0 = 0
1:2/0 = 0
1:2/0/custom_data_0 = true
2:2/0 = 0
2:2/0/custom_data_0 = true
3:2/0 = 0
3:2/0/custom_data_0 = true
4:2/0 = 0
4:2/0/custom_data_0 = true
2:4/size_in_atlas = Vector2i(2, 1)
2:4/0 = 0
4:4/size_in_atlas = Vector2i(2, 1)
4:4/0 = 0
6:4/size_in_atlas = Vector2i(2, 1)
6:4/0 = 0
6:2/0 = 0
1:5/0 = 0
0:6/size_in_atlas = Vector2i(2, 1)
0:6/0 = 0
6:1/0 = 0
6:1/0/custom_data_0 = true
7:1/0 = 0
7:1/0/custom_data_0 = true
4:0/size_in_atlas = Vector2i(1, 2)
4:0/0 = 0
5:0/size_in_atlas = Vector2i(1, 2)
5:0/0 = 0
3:0/0/custom_data_0 = true
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_7jeam"]
texture = ExtResource("2_o4fdg")
0:0/0 = 0
1:0/0 = 0
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_1og8x"]
texture = ExtResource("3_xap0v")
0:0/0 = 0
0:0/0/custom_data_1 = 10
0:0/0/custom_data_1 = 100
1:0/0 = 0
1:0/0/custom_data_1 = 20
2:1/0 = 0
1:1/0 = 0
0:1/0 = 0
3:1/0 = 0
2:0/0 = 0
1:0/0/custom_data_1 = -200
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_i41cv"]
texture = ExtResource("4_f38wc")
0:0/0 = 0
1:0/0 = 0
2:0/0 = 0
3:0/0 = 0
[resource]
custom_data_layer_0/name = "walkable"
custom_data_layer_0/type = 1
custom_data_layer_1/name = "temperature"
custom_data_layer_1/type = 2
custom_data_layer_2/name = "cost"
custom_data_layer_2/type = 2
sources/0 = SubResource("TileSetAtlasSource_114re")
sources/1 = SubResource("TileSetAtlasSource_7jeam")
sources/2 = SubResource("TileSetAtlasSource_1og8x")
sources/3 = SubResource("TileSetAtlasSource_i41cv")
sources/1 = SubResource("TileSetAtlasSource_x77e4")

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

269
project/player.tscn 100644
View File

@ -0,0 +1,269 @@
[gd_scene load_steps=54 format=3 uid="uid://bl8nkx6u5rhng"]
[ext_resource type="Script" path="res://sl_critical_survival.gd" id="1_hnqco"]
[ext_resource type="Script" path="res://sl_hunger_check.gd" id="2_o4vxg"]
[ext_resource type="Script" path="res://sq_shelter_food.gd" id="3_lihsx"]
[ext_resource type="Script" path="res://close_to_shelter.gd" id="4_h6eyg"]
[ext_resource type="Script" path="res://shelter_has_food.gd" id="5_7w1rx"]
[ext_resource type="Script" path="res://go_to_shelter.gd" id="6_42nyb"]
[ext_resource type="Script" path="res://eat_food.gd" id="7_fbip3"]
[ext_resource type="Script" path="res://sq_nearby_food.gd" id="8_ww8in"]
[ext_resource type="Script" path="res://find_food.gd" id="9_0x8qp"]
[ext_resource type="Script" path="res://go_to_target.gd" id="10_7i7ym"]
[ext_resource type="Script" path="res://sl_thirst_check.gd" id="11_ffdn0"]
[ext_resource type="Script" path="res://sl_temperature_check.gd" id="12_ydhmm"]
[ext_resource type="Script" path="res://sl_is_cold.gd" id="13_so5mw"]
[ext_resource type="Script" path="res://sq_has_stick.gd" id="14_0kffm"]
[ext_resource type="Script" path="res://has_stick.gd" id="15_byful"]
[ext_resource type="Script" path="res://enlighten.gd" id="16_2l3xa"]
[ext_resource type="Script" path="res://sq_has_no_stick.gd" id="17_tiljc"]
[ext_resource type="Script" path="res://item_in_range.gd" id="18_s00bm"]
[ext_resource type="Script" path="res://pick_up_item.gd" id="19_vluq6"]
[ext_resource type="Script" path="res://sq_leave_cold_area.gd" id="20_p3pje"]
[ext_resource type="Script" path="res://find_warm_tile.gd" id="21_67q3n"]
[ext_resource type="Script" path="res://go_to_location.gd" id="22_xcsgg"]
[ext_resource type="Script" path="res://sl_boat.gd" id="23_eih7m"]
[ext_resource type="Script" path="res://sq_build_boat.gd" id="24_felg1"]
[ext_resource type="Script" path="res://reached_building_location.gd" id="25_48esn"]
[ext_resource type="Script" path="res://drop_item.gd" id="26_0romh"]
[ext_resource type="Script" path="res://sq_deliver_part.gd" id="27_nq6nb"]
[ext_resource type="Script" path="res://item_in_inventory.gd" id="28_fovnl"]
[ext_resource type="Script" path="res://go_to_boat.gd" id="29_1h5dr"]
[ext_resource type="Script" path="res://sq_pick_up_part.gd" id="30_0enwg"]
[ext_resource type="Script" path="res://part_in_pick_up_range.gd" id="31_is80o"]
[ext_resource type="Script" path="res://sq_boat_part_found.gd" id="32_adtgi"]
[ext_resource type="Script" path="res://boat_partin_sight.gd" id="33_jfhod"]
[ext_resource type="Script" path="res://sl_base_survival.gd" id="34_hvb4y"]
[ext_resource type="Script" path="res://sq_hunger_check.gd" id="35_pi8ok"]
[ext_resource type="Script" path="res://food_in_range.gd" id="36_u1dns"]
[ext_resource type="Script" path="res://sq_thirst_check.gd" id="37_ax36q"]
[ext_resource type="Script" path="res://sq_sleep_check.gd" id="38_5yc81"]
[ext_resource type="Script" path="res://needs_sleep.gd" id="39_jvpr1"]
[ext_resource type="Script" path="res://sq_campfire.gd" id="40_j82mx"]
[ext_resource type="Script" path="res://sq_shelter_sticks.gd" id="41_kypmr"]
[ext_resource type="Script" path="res://sq_nearby_sticks.gd" id="42_78h70"]
[ext_resource type="Script" path="res://find_sticks.gd" id="43_iwbka"]
[ext_resource type="Script" path="res://bring_to_shelter.gd" id="44_sive8"]
[ext_resource type="Script" path="res://make_fire.gd" id="45_4mjsr"]
[ext_resource type="Script" path="res://sleep.gd" id="46_5nso6"]
[ext_resource type="Script" path="res://sl_explore.gd" id="47_pe2pv"]
[ext_resource type="Script" path="res://sl_hoarding.gd" id="48_l4nhg"]
[ext_resource type="Script" path="res://sq_berries.gd" id="49_w5v2i"]
[ext_resource type="Script" path="res://stock_not_full.gd" id="50_pjjqc"]
[ext_resource type="Script" path="res://target_in_range.gd" id="51_7qc80"]
[ext_resource type="Script" path="res://sq_sticks.gd" id="52_v57vv"]
[ext_resource type="Script" path="res://sq_exploring.gd" id="53_nb8c6"]
[node name="Player" type="Node2D"]
[node name="DecisionTree" type="Node" parent="."]
[node name="Sl_CriticalSurvival" type="Node" parent="DecisionTree"]
script = ExtResource("1_hnqco")
[node name="Sl_HungerCheck" type="Node" parent="DecisionTree/Sl_CriticalSurvival"]
script = ExtResource("2_o4vxg")
[node name="Sq_ShelterFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck"]
script = ExtResource("3_lihsx")
[node name="CloseToShelter" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_ShelterFood"]
script = ExtResource("4_h6eyg")
[node name="ShelterHasFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_ShelterFood"]
script = ExtResource("5_7w1rx")
[node name="GoToShelter" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_ShelterFood"]
script = ExtResource("6_42nyb")
[node name="EatFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_ShelterFood"]
script = ExtResource("7_fbip3")
[node name="Sq_NearbyFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck"]
script = ExtResource("8_ww8in")
[node name="FindFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_NearbyFood"]
script = ExtResource("9_0x8qp")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_NearbyFood"]
script = ExtResource("10_7i7ym")
[node name="EatFood" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_HungerCheck/Sq_NearbyFood"]
script = ExtResource("7_fbip3")
[node name="Sl_ThirstCheck" type="Node" parent="DecisionTree/Sl_CriticalSurvival"]
script = ExtResource("11_ffdn0")
[node name="Sl_TemperatureCheck" type="Node" parent="DecisionTree/Sl_CriticalSurvival"]
script = ExtResource("12_ydhmm")
[node name="Sl_IsCold" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck"]
script = ExtResource("13_so5mw")
[node name="Sq_HasStick" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold"]
script = ExtResource("14_0kffm")
[node name="HasStick" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_HasStick"]
script = ExtResource("15_byful")
[node name="Enlighten" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_HasStick"]
script = ExtResource("16_2l3xa")
[node name="Sq_HasNoStick" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold"]
script = ExtResource("17_tiljc")
[node name="ItemInRange" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_HasNoStick"]
script = ExtResource("18_s00bm")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_HasNoStick"]
script = ExtResource("10_7i7ym")
[node name="PickUpItem" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_HasNoStick"]
script = ExtResource("19_vluq6")
[node name="Sq_LeaveColdArea" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold"]
script = ExtResource("20_p3pje")
[node name="FindWarmTile" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_LeaveColdArea"]
script = ExtResource("21_67q3n")
[node name="GoToLocation" type="Node" parent="DecisionTree/Sl_CriticalSurvival/Sl_TemperatureCheck/Sl_IsCold/Sq_LeaveColdArea"]
script = ExtResource("22_xcsgg")
[node name="Sl_Boat" type="Node" parent="DecisionTree"]
script = ExtResource("23_eih7m")
[node name="Sq_BuildBoat" type="Node" parent="DecisionTree/Sl_Boat"]
script = ExtResource("24_felg1")
[node name="ReachedBuildingLocation" type="Node" parent="DecisionTree/Sl_Boat/Sq_BuildBoat"]
script = ExtResource("25_48esn")
[node name="DropItem" type="Node" parent="DecisionTree/Sl_Boat/Sq_BuildBoat"]
script = ExtResource("26_0romh")
[node name="Sq_DeliverPart" type="Node" parent="DecisionTree/Sl_Boat"]
script = ExtResource("27_nq6nb")
[node name="ItemInInventory" type="Node" parent="DecisionTree/Sl_Boat/Sq_DeliverPart"]
script = ExtResource("28_fovnl")
[node name="GoToBoat" type="Node" parent="DecisionTree/Sl_Boat/Sq_DeliverPart"]
script = ExtResource("29_1h5dr")
[node name="Sq_PickUpPart" type="Node" parent="DecisionTree/Sl_Boat"]
script = ExtResource("30_0enwg")
[node name="PartInPickUpRange" type="Node" parent="DecisionTree/Sl_Boat/Sq_PickUpPart"]
script = ExtResource("31_is80o")
[node name="PickUpItem" type="Node" parent="DecisionTree/Sl_Boat/Sq_PickUpPart"]
script = ExtResource("19_vluq6")
[node name="Sq_BoatPartFound" type="Node" parent="DecisionTree/Sl_Boat"]
script = ExtResource("32_adtgi")
[node name="BoatPartinSight" type="Node" parent="DecisionTree/Sl_Boat/Sq_BoatPartFound"]
script = ExtResource("33_jfhod")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_Boat/Sq_BoatPartFound"]
script = ExtResource("10_7i7ym")
[node name="Sl_BaseSurvival" type="Node" parent="DecisionTree"]
script = ExtResource("34_hvb4y")
[node name="Sq_HungerCheck" type="Node" parent="DecisionTree/Sl_BaseSurvival"]
script = ExtResource("35_pi8ok")
[node name="FoodInRange" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_HungerCheck"]
script = ExtResource("36_u1dns")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_HungerCheck"]
script = ExtResource("10_7i7ym")
[node name="EatFood" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_HungerCheck"]
script = ExtResource("7_fbip3")
[node name="Sq_ThirstCheck" type="Node" parent="DecisionTree/Sl_BaseSurvival"]
script = ExtResource("37_ax36q")
[node name="Sq_SleepCheck" type="Node" parent="DecisionTree/Sl_BaseSurvival"]
script = ExtResource("38_5yc81")
[node name="NeedsSleep" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck"]
script = ExtResource("39_jvpr1")
[node name="GoToShelter" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck"]
script = ExtResource("6_42nyb")
[node name="Sq_Campfire" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck"]
script = ExtResource("40_j82mx")
[node name="Sq_ShelterSticks" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire"]
script = ExtResource("41_kypmr")
[node name="PickUpItem" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire/Sq_ShelterSticks"]
script = ExtResource("19_vluq6")
[node name="Sq_NearbySticks" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire"]
script = ExtResource("42_78h70")
[node name="FindSticks" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire/Sq_NearbySticks"]
script = ExtResource("43_iwbka")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire/Sq_NearbySticks"]
script = ExtResource("10_7i7ym")
[node name="BringToShelter" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire/Sq_NearbySticks"]
script = ExtResource("44_sive8")
[node name="MakeFire" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire"]
script = ExtResource("45_4mjsr")
[node name="Sleep" type="Node" parent="DecisionTree/Sl_BaseSurvival/Sq_SleepCheck/Sq_Campfire"]
script = ExtResource("46_5nso6")
[node name="Sl_Explore" type="Node" parent="DecisionTree"]
script = ExtResource("47_pe2pv")
[node name="Sl_Hoarding" type="Node" parent="DecisionTree/Sl_Explore"]
script = ExtResource("48_l4nhg")
[node name="Sq_Berries" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding"]
script = ExtResource("49_w5v2i")
[node name="CloseToShelter" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Berries"]
script = ExtResource("4_h6eyg")
[node name="StockNotFull" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Berries"]
script = ExtResource("50_pjjqc")
[node name="TargetInRange" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Berries"]
script = ExtResource("51_7qc80")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Berries"]
script = ExtResource("10_7i7ym")
[node name="BringToShelter" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Berries"]
script = ExtResource("44_sive8")
[node name="Sq_Sticks" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding"]
script = ExtResource("52_v57vv")
[node name="CloseToShelter" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Sticks"]
script = ExtResource("4_h6eyg")
[node name="StockNotFull" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Sticks"]
script = ExtResource("50_pjjqc")
[node name="TargetInRange" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Sticks"]
script = ExtResource("51_7qc80")
[node name="GoToTarget" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Sticks"]
script = ExtResource("10_7i7ym")
[node name="BringToShelter" type="Node" parent="DecisionTree/Sl_Explore/Sl_Hoarding/Sq_Sticks"]
script = ExtResource("44_sive8")
[node name="Exploring" type="Node" parent="DecisionTree/Sl_Explore"]
script = ExtResource("53_nb8c6")

View File

@ -66,65 +66,13 @@ key_2={
"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":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null)
]
}
key_3={
"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":51,"key_label":0,"unicode":51,"location":0,"echo":false,"script":null)
]
}
key_9={
"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":57,"key_label":0,"unicode":57,"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)
]
}
key_7={
"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":55,"key_label":0,"unicode":55,"location":0,"echo":false,"script":null)
]
}
key_5={
"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":53,"key_label":0,"unicode":53,"location":0,"echo":false,"script":null)
]
}
key_6={
"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":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)

View File

@ -0,0 +1,11 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

View File

@ -1,14 +1,12 @@
class_name CameraController
class_name Camera
extends Camera2D
@onready var shader_vignette: ColorRect = $CanvasLayer/Vignette
@export var border_acceleration: float = 2000.0
@export var border_acceleration: float = 800.0
@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(874, 843)
@export var max_position: Vector2 = Vector2(4884, 4623)
@export var inner_border_threshold: float = 60.0
@export var outer_border_threshold: float = 20.0
@export var min_position: Vector2 = Vector2(-1000, -1000)
@export var max_position: Vector2 = Vector2(4000, 4000)
var velocity: Vector2 = Vector2.ZERO
#
@ -31,11 +29,7 @@ func go_to(position: Vector2) -> void:
func print_config() -> void:
print("camera.go_to_zooming(Vector2(", position.x, ", ", position.y, "), ", zoom.x, ")")
func map_range(value: float, from_min: float, from_max: float, to_min: float, to_max: float) -> float:
return to_min + (value - from_min) / (from_max - from_min) * (to_max - to_min)
print("camera.go_to_zooming(Vector2(", position.x, ", ", position.y, "), ", zoom, ")")
func _input(event):
@ -76,9 +70,9 @@ func _process(delta):
var is_zoom_out: bool = Input.is_action_pressed("camera_zoom_out")
if is_zoom_in:
zoom = zoom * 1.02
zoom = zoom * 1.1
elif is_zoom_out:
zoom = zoom / 1.02
zoom = zoom / 1.1
if zoom.length() < 0.2:
zoom = Vector2(1, 1).normalized() * 0.2
@ -89,13 +83,10 @@ func _process(delta):
var screen_size: Vector2 = get_viewport().get_visible_rect().size
var acceleration: Vector2 = Vector2.ZERO
# the bigger the viewport size, the bigger the border threshold
var border_threshold_addition: float = max(0, map_range(screen_size.length(), 1320, 2600, 0, 100))
var is_up: bool = Input.is_action_pressed("camera_up") or mouse_pos.y < inner_border_threshold + border_threshold_addition and mouse_pos.y > -outer_border_threshold
var is_down: bool = Input.is_action_pressed("camera_down") or mouse_pos.y > screen_size.y - inner_border_threshold - border_threshold_addition and mouse_pos.y < screen_size.y + outer_border_threshold
var is_left: bool = Input.is_action_pressed("camera_left") or mouse_pos.x < inner_border_threshold + border_threshold_addition and mouse_pos.x > -outer_border_threshold
var is_right: bool = Input.is_action_pressed("camera_right") or mouse_pos.x > screen_size.x - inner_border_threshold - border_threshold_addition and mouse_pos.x < screen_size.x + outer_border_threshold
var is_up: bool = Input.is_action_pressed("camera_up") or mouse_pos.y < inner_border_threshold and mouse_pos.y > -outer_border_threshold
var is_down: bool = Input.is_action_pressed("camera_down") or mouse_pos.y > screen_size.y - inner_border_threshold and mouse_pos.y < screen_size.y + outer_border_threshold
var is_left: bool = Input.is_action_pressed("camera_left") or mouse_pos.x < inner_border_threshold and mouse_pos.x > -outer_border_threshold
var is_right: bool = Input.is_action_pressed("camera_right") or mouse_pos.x > screen_size.x - inner_border_threshold and mouse_pos.x < screen_size.x + outer_border_threshold
if is_left:
acceleration.x = -border_acceleration
@ -127,10 +118,3 @@ func _process(delta):
var offset: Vector2 = target_position - position
position += offset
# SECTION: shader access
func set_vignette_intensity(value: float) -> void:
var material: ShaderMaterial = shader_vignette.material
material.set_shader_parameter("vignette_strength", value)

View File

@ -1,112 +0,0 @@
class_name EventsTracker
extends Node
enum Event {
CAMPFIRE_LIT,
CAMPFIRE_LIT_FAILED,
CAMPFIRE_EXTINGUISHED,
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 = 14
static var callbacks: Array[Callable] = []
static func track(event: Event, params: Dictionary = {}) -> void:
var tracked_event: TrackedEvent = TrackedEvent.new()
tracked_event.event = event
tracked_event.params = params
events.append(tracked_event)
print("Event tracked: ", event, " params: ", params)
if events.size() > max_events:
events.remove_at(0)
for cb in callbacks:
cb.call(event, params)
static func populate_visual_log(visual_events_log: VBoxContainer, game_manager: GameManager) -> void:
for child in visual_events_log.get_children():
child.queue_free()
for event in events:
event = event as TrackedEvent
populate_visual_log_create_label(event, visual_events_log, game_manager)
static func populate_visual_log_create_label(event: TrackedEvent, container: Container, game_manager: GameManager) -> void:
var event_id: int = event.event
var params: Dictionary = event.params
var text: String = ""
if event_id == Event.CAMPFIRE_LIT:
text = "Campfire lit"
elif event_id == Event.CAMPFIRE_LIT_FAILED:
text = "Campfire lighting failed"
elif event_id == Event.CAMPFIRE_EXTINGUISHED:
text = "Campfire extinguished"
elif event_id == Event.CAMP_ADDED_ITEM:
text = "Camp added item"
elif event_id == Event.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"
elif event_id == Event.PLAYER_DROPPED_ITEM:
text = "Dropped"
elif event_id == Event.PLAYER_USED_ITEM:
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()
event_container.add_child(event_label)
if params.has("item"):
var item_texture: Texture = game_manager.world.tilemap_interactive.get_cell_texture(params["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)
event_container.add_child(item_texture_rect)
container.add_child(event_container)

View File

@ -1,295 +1,17 @@
class_name GameManager
extends Node
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
@onready var world: World = $Tileset
@onready var player: PlayerManager = $PlayerManager
@onready var camera: CameraController = $Camera2D as CameraController
@onready var game_ticker: Timer = $GameTick
#
@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
@onready var camera: Camera2D = $Camera2D
func _ready() -> void:
tilemap_navigation.world = world
tilemap_navigation.player = player
player.game_manager = self
world.camp_manager.game_manager = self
world.step_visualizer.game_manager = self
world.step_visualizer.world = world
update_bars()
call_deferred("defer_ready")
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)
pass
func _process(delta: float) -> void:
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("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
pass
func _on_game_tick_timeout() -> void:
var timer_on_game_tick_timeout: PerformanceTimer = PerformanceTimer.new()
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)
%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)
%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_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:
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
if Input.is_action_just_pressed("key_1"):
camera.go_to_zooming(Vector2(-100, -50), 1.5)
if Input.is_action_just_pressed("key_2"):
camera.go_to_zooming(Vector2(200, 100), 0.4)
if Input.is_action_just_pressed("key_9"):
camera.print_config()

View File

@ -1,5 +0,0 @@
class_name TrackedEvent
extends Node
var event: EventsTracker.Event = -1
var params: Dictionary = {}

View File

@ -1,248 +1,2 @@
class_name PlayerManager
extends Node
@export var max_health: int = 100
# food system
@export var max_food: int = 250
@export var food_damage: int = 1
@export var food_critical_threshold: int = 50
@export var food_base_threshold: int = 150
# temperature
@export var temperature_set_buff_value: int = 150
@export var temperature_damage: int = 1
@export var temperature_endure: int = 50
# viewing
@export var view_distance: int = 50
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
#
var game_manager: GameManager = null
var last_board_position: Vector2i = Vector2i(0, 0)
var board_position: Vector2i = Vector2i(0, 0):
set(value):
last_board_position = board_position
board_position = value
update_board()
@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")
func defer_ready() -> void:
behavior_tree.game_manager = game_manager
var player_start_position: Array[Vector2i] = game_manager.world.tilemap_player.get_cells_by_type(tilemap_types.PLAYER_DOWN)
if len(player_start_position) > 0:
board_position = player_start_position[0]
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_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))
pick_up_item(Vector2i(9, 9))
if Input.is_action_just_pressed("key_4"):
var nearest: Vector2i = find_nearest_object([game_manager.world.tilemap_types.OBJECT_I_TREE_FULL])
# nearest.x = nearest.x - 1
walk_towards(nearest)
# SECTION: board access/mangement
func update_board() -> void:
game_manager.world.tilemap_player.clear_cells()
# decide what direction the player is facing tilemap_types.PLAYER_DOWN, ...
var direction: Vector2i = find_direction(last_board_position, board_position)
game_manager.world.tilemap_player.set_cell(board_position, tilemap_types.player_sprite_from_direction(direction))
if inventory_slot and inventory_slot != tilemap_types.EMPTY:
%InventoryContentRect.texture = game_manager.world.tilemap_interactive.get_cell_texture(inventory_slot)
else:
%InventoryContentRect.texture = null
# SECTION: inventory system
func pick_up_item(tilemap_pos: Vector2i) -> void:
var pick_up_cell: TileData = game_manager.world.tilemap_interactive.get_cell(tilemap_pos)
if not pick_up_cell:
push_warning("Player trying to pick up item that does not exist at ", tilemap_pos)
return
var pick_up_item_type: Vector2i = game_manager.world.tilemap_interactive.tilemap.get_cell_atlas_coords(tilemap_pos)
# 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
pick_up_item_type = tilemap_types.OBJECT_I_BERRY
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
# check if the inventory slot is empty
if inventory_slot == tilemap_types.EMPTY:
inventory_slot = pick_up_item_type
if tile_after_pickup_transform:
game_manager.world.tilemap_interactive.set_cell(tilemap_pos, tile_after_pickup_transform)
else:
game_manager.world.tilemap_interactive.clear_cell(tilemap_pos)
print("Picked up item: ", pick_up_item_type)
EventsTracker.track(EventsTracker.Event.PLAYER_PICKED_UP_ITEM, {"item": pick_up_item_type})
else:
# inventory is full, swap the item
print("Inventory is full. Swapping item: ", inventory_slot, " with item: ", pick_up_item_type)
EventsTracker.track(EventsTracker.Event.PLAYER_DROPPED_ITEM, {"item": inventory_slot})
EventsTracker.track(EventsTracker.Event.PLAYER_PICKED_UP_ITEM, {"item": pick_up_item_type})
if tile_after_pickup_transform:
game_manager.world.tilemap_interactive.set_cell(tilemap_pos, tile_after_pickup_transform)
var drop_location: Vector2i = game_manager.world.find_item_drop_location(tilemap_pos)
if drop_location != tilemap_types.EMPTY:
game_manager.world.tilemap_interactive.set_cell(drop_location, tile_drop_item)
else:
push_warning("Could not find valid drop position for ", inventory_slot)
else:
game_manager.world.tilemap_interactive.set_cell(tilemap_pos, tile_drop_item)
inventory_slot = pick_up_item_type
# SECTION: player movement
func walk_towards(position: Vector2i) -> void:
var path: Array[Vector2i] = game_manager.tilemap_navigation.find_path(board_position, position)
walk_along(path)
func walk_along(path: Array[Vector2i]) -> void:
if len(path) > 1:
var next_position: Vector2i
if path.has(board_position):
var current_index: int = path.find(board_position)
if current_index < path.size() - 1:
next_position = path[current_index + 1]
else:
next_position = path[1]
else:
next_position = path[1]
var direction: Vector2i = find_direction(board_position, next_position)
move_player(direction)
game_manager.tilemap_navigation.chosen_path = path
else:
push_warning("walk_along path is empty")
func move_player(direction: Vector2i) -> void:
var new_position: Vector2 = board_position + direction
if game_manager.world.is_walkable(new_position):
board_position = new_position
else:
push_warning("Player trying to move to non-walkable position, prevented ", new_position)
func find_nearest_object(object_collection: Array[Vector2i]) -> Vector2i:
var object_positions: Array[Vector2i] = []
for obj in object_collection:
object_positions.append_array(game_manager.world.tilemap_interactive.get_cells_by_type(obj))
if object_positions.size() == 0:
push_warning("No " + str(object_collection) + " found!")
return tilemap_types.NO_TILE_FOUND
var closest_object: Vector2i = tilemap_types.NO_TILE_FOUND
var shortest_distance: float = 99999999
for position in object_positions:
var distance: float = game_manager.tilemap_navigation.manhattan_distance(board_position, position)
if closest_object == tilemap_types.NO_TILE_FOUND or distance < shortest_distance:
closest_object = position
shortest_distance = distance
print("Find nearest " + str(object_collection) + " at:", closest_object)
return closest_object
func find_direction(pos_a: Vector2i, pos_b: Vector2i) -> Vector2i:
var direction: Vector2i = Vector2i(0, 0)
if pos_a.x < pos_b.x:
direction.x = 1
elif pos_a.x > pos_b.x:
direction.x = -1
if pos_a.y < pos_b.y:
direction.y = 1
elif pos_a.y > pos_b.y:
direction.y = -1
return direction
# SECTION: game tick
func get_current_temperature() -> int:
return game_manager.world.tilemap_temperature.get_custom_data(board_position, "temperature", 0) as int
func tick_handle_temperature(cell_temperature: int) -> void:
if temperature_buff_timer > 0:
temperature_buff_timer -= 1
return
if cell_temperature == 0:
temperature_timer = 0
elif temperature_timer > temperature_endure:
temperature_timer += cell_temperature
health -= temperature_damage
func tick_handle_food():
if food > 0:
food -= 1
if food <= 0:
health -= food_damage
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()
if health < 0:
game_manager.player_health_depleted()
update_board()
extends Node

View File

@ -1,41 +0,0 @@
class_name BehaviorTree
extends Node
var game_manager: GameManager = null
#
var blackboard: Dictionary = {}
var behavior_tree: Task = null
func _ready() -> void:
if get_child_count() == 0 or get_child_count() > 1:
push_error("This controller needs exactly one Task child, got " + str(get_child_count()))
var child: Node = get_child(0)
if not (child is Task):
push_error("Child is not a task: " + child.name)
behavior_tree = child as Task
initialize_blackboard()
func initialize_blackboard() -> void:
blackboard["cached_paths"] = {}
func populate_blackboard():
blackboard["world"] = game_manager.world
blackboard["player"] = game_manager.player
blackboard["camera"] = game_manager.camera
blackboard["navigation"] = game_manager.tilemap_navigation
func game_tick() -> void:
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)

View File

@ -1,133 +0,0 @@
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()
func _ready() -> void:
for c in get_children():
if not c is Task:
push_error("Child is not a task: " + c.name + " in " + name)
return
func internal_run(blackboard: Dictionary) -> void:
blackboard["current_task"] = self
var running_child: Task = find_running_child()
var extra_string: String = ""
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))
func find_running_child() -> Task:
for c in get_children():
if c.status == RUNNING:
return c
return null
func run_child(blackboard: Dictionary, p_child: Task) -> void:
p_child.internal_run(blackboard)
if p_child.status != RUNNING:
status = RUNNING
func slice_at_child(start_child: Task) -> Array:
var children: Array[Node] = get_children()
if start_child == null:
return children
var start_index: int = children.find(start_child)
if start_index == -1:
return children
return children.slice(start_index, children.size())
func run(blackboard: Dictionary) -> void:
pass
func cancel(blackboard: Dictionary):
pass
func get_first_child() -> Task:
if get_child_count() == 0:
push_error("Task does not have any children: " + name)
return null
return get_children()[0] as Task
func human_readable(addon: String = "") -> String:
var clear_status: String = clear_status()
var ret: String = name;
if addon != "":
ret += " " + addon
if status_reason != "":
ret += " [" + clear_status + ", " + status_reason + "]"
else:
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

View File

@ -1,28 +0,0 @@
class_name GoToRunning
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if not blackboard.has("path"):
status = FAILURE
status_reason = "blackboard did not have path"
return
var path: Array[Vector2i] = blackboard["path"]
if path.size() == 0:
status = FAILURE
status_reason = "path was empty"
return
player.walk_along(path)
if navigation.has_arrived(player.board_position, path):
blackboard["cached_paths"] = {}
status = SUCCESS
status_reason = "already arrived at destination"
return
status = RUNNING
status_reason = "walked along path, now at " + str(player.board_position)

View File

@ -1,28 +0,0 @@
class_name GoToSuccess
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if not blackboard.has("path"):
status = FAILURE
status_reason = "blackboard did not have path"
return
var path: Array[Vector2i] = blackboard["path"]
if path.size() == 0:
status = FAILURE
status_reason = "path was empty"
return
player.walk_along(path)
if navigation.has_arrived(player.board_position, path):
blackboard["cached_paths"] = {}
status = SUCCESS
status_reason = "already arrived at destination"
return
status = SUCCESS
status_reason = "walked along path, now at " + str(player.board_position)

View File

@ -1,28 +0,0 @@
class_name GoToWhileRunningSuccessStop
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if not blackboard.has("path"):
status = FAILURE
status_reason = "blackboard did not have path"
return
var path: Array[Vector2i] = blackboard["path"]
if path.size() == 0:
status = FAILURE
status_reason = "path was empty"
return
player.walk_along(path)
if navigation.has_arrived(player.board_position, path):
blackboard["cached_paths"] = {}
status = SUCCESS
status_reason = "already arrived at destination"
return
status = SUCCESS_STOP
status_reason = "walked along path, now at " + str(player.board_position)

View File

@ -1,23 +0,0 @@
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 " + c.name + " (STOP)"
return
if c.status != FAILURE:
status = c.status
status_reason = "stopped at " + c.name
return
status = FAILURE
status_reason = "all children failed"

View File

@ -1,23 +0,0 @@
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 " + c.name + " (STOP)"
return
if c.status != SUCCESS:
status = c.status
status_reason = "stopped at " + c.name
return
status = SUCCESS
status_reason = "all children succeeded"

View File

@ -1,114 +0,0 @@
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

View File

@ -1,14 +0,0 @@
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)

View File

@ -1,13 +0,0 @@
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) + ")"

View File

@ -1,13 +0,0 @@
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"

View File

@ -1,20 +0,0 @@
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"

View File

@ -1,31 +0,0 @@
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)

View File

@ -1,17 +0,0 @@
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)

View File

@ -1,25 +0,0 @@
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)

View File

@ -1,12 +0,0 @@
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

View File

@ -1,13 +0,0 @@
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"

View File

@ -1,24 +0,0 @@
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"

View File

@ -1,19 +0,0 @@
class_name TaskFindCamp
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
blackboard["location_camp"] = world.camp_manager.camp
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
status_reason = "Found camp at " + str(blackboard["location_camp"])
return
status = FAILURE
status_reason = "No path found to camp " + str(blackboard["location_camp"])

View File

@ -1,25 +0,0 @@
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)

View File

@ -1,16 +0,0 @@
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"

View File

@ -1,13 +0,0 @@
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)

View File

@ -1,15 +0,0 @@
class_name TaskCheckFoodCritical
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if player.food > player.food_critical_threshold:
status = FAILURE
status_reason = "Player food is not critical (" + str(player.food) + " > " + str(player.food_critical_threshold) + ")"
return
status = SUCCESS
status_reason = "Player food is critical (" + str(player.food) + " <= " + str(player.food_critical_threshold) + ")"

View File

@ -1,27 +0,0 @@
class_name TaskCheckTemperatureCold
extends Task
var last_temperature: float = 0
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
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(current_temperature)
return
status = FAILURE
status_reason = "not cold: " + str(current_temperature)

View File

@ -1,13 +0,0 @@
class_name TaskCheckTemperatureNoBuff
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
if player.temperature_buff_timer > 0:
status = FAILURE
status_reason = "Player already has a temperature buff: " + str(player.temperature_buff_timer)
return
status = SUCCESS
status_reason = "Player does not have a temperature buff"

View File

@ -1,33 +0,0 @@
class_name TaskFindClosestFood
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
var active_foods: Array[Vector2i] = world.tilemap_interactive.get_cells_by_type_collection(
tilemap_types.OBJECT_COLLECTION_BERRY_SOURCE, player.board_position, player.view_distance)
if len(active_foods) == 0:
status = FAILURE
status_reason = "No active foods found"
return
var closest_berry: Vector2i = navigation.manhattan_distance_closest(active_foods, player.board_position)
StepVisualization.add_line_tileset(player.board_position, closest_berry, StepVisualization.LineType.SEARCH_SELECTED)
if closest_berry == tilemap_types.NO_TILE_FOUND:
status = FAILURE
status_reason = "No closest berry found"
return
blackboard["closest_berry"] = closest_berry
var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, closest_berry, player.view_distance)
if path.size() > 0:
blackboard["path"] = path
status_reason = "Found path to closest berry"
status = SUCCESS
return
status = FAILURE
status_reason = "No path found to closest berry " + str(closest_berry)

View File

@ -1,33 +0,0 @@
class_name TaskFindClosestStick
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
var sticks: Array[Vector2i] = world.tilemap_interactive.get_cells_by_type_collection(
tilemap_types.OBJECT_COLLECTION_STICK_SOURCE, player.board_position, player.view_distance)
if len(sticks) == 0:
status = FAILURE
status_reason = "No active sticks found"
return
var closest_stick: Vector2i = navigation.manhattan_distance_closest(sticks, player.board_position)
StepVisualization.add_line_tileset(player.board_position, closest_stick, StepVisualization.LineType.SEARCH_SELECTED)
if closest_stick == tilemap_types.NO_TILE_FOUND:
status = FAILURE
status_reason = "No closest stick found"
return
blackboard["closest_stick"] = closest_stick
var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, closest_stick, player.view_distance)
if path.size() > 0:
blackboard["path"] = path
status_reason = "Found path to closest stick"
status = SUCCESS
return
status = FAILURE
status_reason = "No path found to closest stick " + str(closest_stick)

View File

@ -1,35 +0,0 @@
class_name TaskFindClosestWarmTile
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
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,
false)
if len(warm_tiles) == 0:
status = FAILURE
status_reason = "No warm tiles found"
return
var closest_warm_tile: Vector2i = navigation.manhattan_distance_closest(warm_tiles, player.board_position)
StepVisualization.add_line_tileset(player.board_position, closest_warm_tile, StepVisualization.LineType.SEARCH_SELECTED)
if closest_warm_tile == tilemap_types.NO_TILE_FOUND:
status = FAILURE
status_reason = "No closest warm tile found"
return
blackboard["closest_warm_tile"] = closest_warm_tile
var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, closest_warm_tile, player.view_distance)
if path.size() > 0:
blackboard["path"] = path
status_reason = "Found path to closest warm tile"
status = SUCCESS
return
status = FAILURE
status_reason = "No path found to closest warm tile " + str(closest_warm_tile)

View File

@ -1,17 +0,0 @@
class_name TaskEatFoodFromInventory
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
if player.inventory_slot != tilemap_types.OBJECT_I_BERRY:
status = FAILURE
status_reason = "Inventory does not contain berry"
return
EventsTracker.track(EventsTracker.Event.PLAYER_USED_ITEM, {"item": player.inventory_slot})
player.inventory_slot = tilemap_types.EMPTY
player.food = player.max_food
status = SUCCESS
status_reason = "Ate berry, player now has " + str(player.food) + " food"

View File

@ -1,13 +0,0 @@
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"

View File

@ -1,13 +0,0 @@
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"

View File

@ -1,13 +0,0 @@
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"

View File

@ -1,16 +0,0 @@
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"

View File

@ -1,12 +0,0 @@
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"

View File

@ -1,11 +0,0 @@
class_name TaskPickupStick
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var closest_stick: Vector2i = blackboard["closest_stick"]
player.pick_up_item(closest_stick)
status = SUCCESS
status_reason = "Picked up stick"

View File

@ -1,11 +0,0 @@
class_name TaskPickupTheBerry
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var closest_berry: Vector2i = blackboard["closest_berry"]
player.pick_up_item(closest_berry)
status = SUCCESS
status_reason = "Picked up berry"

View File

@ -1,16 +0,0 @@
class_name TaskUseStickFromInventory
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
if player.inventory_slot != tilemap_types.OBJECT_I_STICK:
status = FAILURE
status_reason = "Player does not have a stick in inventory"
return
EventsTracker.track(EventsTracker.Event.PLAYER_USED_ITEM, {"item": player.inventory_slot})
player.inventory_slot = tilemap_types.EMPTY
player.temperature_buff_timer = player.temperature_set_buff_value
status = SUCCESS
status_reason = "Player used a stick from inventory, now has temperature buff: " + str(player.temperature_buff_timer)

View File

@ -1,9 +0,0 @@
class_name Task5050Running
extends Task
func run(blackboard: Dictionary) -> void:
var random: int = randi() % 2
if random == 0:
status = RUNNING
else:
status = SUCCESS

View File

@ -1,9 +0,0 @@
class_name Task5050Success
extends Task
func run(blackboard: Dictionary) -> void:
var random: int = randi() % 2
if random == 0:
status = SUCCESS
else:
status = FAILURE

View File

@ -1,14 +0,0 @@
class_name WalkToMouse
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var tilemap_navigation: TilemapNavigation = blackboard["navigation"]
var path: Array[Vector2i] = tilemap_navigation.find_path(player.board_position, world.tilemap_mouse_position(), player.view_distance)
if len(path) == 0:
status = FAILURE
player.walk_along(path)
status = SUCCESS

View File

@ -1,15 +0,0 @@
class_name WalkUpToMouse
extends Task
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
# 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
player.walk_along(path)
status = SUCCESS

View File

@ -1,78 +0,0 @@
class_name StepVisualization
extends Node2D
static var game_manager: GameManager
static var world: World
#
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:
draw_lines[[from, to]] = line_type
static func add_line_tileset(from: Vector2i, to: Vector2i, line_type: LineType) -> void:
var from_tileset: Vector2i = world.tilemap_ground.cell_to_local(from)
var to_tileset: Vector2i = world.tilemap_ground.cell_to_local(to)
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():
queue_redraw()
var label_font = Control.new().get_theme_default_font()
@export var default_color: Color = Color("red")
func _ready() -> void:
pass
func _draw() -> void:
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]
var line_type: LineType = draw_lines[key]
if line_type == LineType.SEARCH_BASE:
draw_line(from, to, Color("blue"), 1)
elif line_type == LineType.SEARCH_SELECTED:
draw_line(from, to, Color("green"), 2)
elif line_type == LineType.SEARCH_FAILED:
draw_line(from, to, Color(255, 0, 0, 0.1), 1)

View File

@ -1,8 +1,6 @@
class_name TileMapLayerAccess
extends Node
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
#
var tilemap: TileMapLayer = null
var sid: int = 0
@ -11,38 +9,8 @@ func setup() -> void:
pass
func get_cells_by_type(
atlas_coords: Vector2i,
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.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
) -> Array[Vector2i]:
var tiles_with_type: Array[Vector2i] = []
for coords in atlas_coords:
tiles_with_type.append_array(get_cells_by_type(coords))
if max_distance < 99999999:
var filtered_tiles: Array[Vector2i] = []
for tile in tiles_with_type:
if TilemapNavigation.is_within_radius(center, tile, max_distance, true):
filtered_tiles.append(tile)
return filtered_tiles
return tiles_with_type
func get_cells_by_type(atlas_coords: Vector2i) -> Array[Vector2i]:
return tilemap.get_used_cells_by_id(sid, atlas_coords)
func get_cells_by_custom_data(field_name: String, custom_data: Variant) -> Array[Vector2i]:
@ -54,10 +22,8 @@ func get_cells_by_custom_data(field_name: String, custom_data: Variant) -> Array
return tiles_with_custom_data
func get_custom_data(coords: Vector2i, field_name: String, default_value: Variant) -> Variant:
func get_custom_data(coords: Vector2i, field_name: String) -> Variant:
var tile_data: TileData = tilemap.get_cell_tile_data(coords)
if not tile_data:
return default_value
return tile_data.get_custom_data(field_name)
@ -72,12 +38,6 @@ func get_cell(position: Vector2i) -> TileData:
return tilemap.get_cell_tile_data(position)
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)
func set_cell(position: Vector2i, atlas_coords: Vector2i) -> void:
tilemap.set_cell(position, sid, atlas_coords)
@ -117,11 +77,3 @@ func fill_ellipse(center: Vector2i, radius_x: int, radius_y: int, atlas_coords:
for y in range(center.y - radius_y, center.y + radius_y + 1):
if (pow(x - center.x, 2) / pow(radius_x, 2) + pow(y - center.y, 2) / pow(radius_y, 2)) <= 1:
tilemap.set_cell(Vector2i(x, y), sid, atlas_coords)
func get_cell_texture(coord: Vector2i) -> Texture:
var source: TileSetAtlasSource = tilemap.tile_set.get_source(sid) as TileSetAtlasSource
var rect: Rect2i = source.get_tile_texture_region(coord)
var image: Image = source.texture.get_image()
var tile_image: Image = image.get_region(rect)
return ImageTexture.create_from_image(tile_image)

View File

@ -1,88 +1,19 @@
class_name TileMapTileTypes
# global values
const EMPTY: Vector2i = Vector2i(-1, -1)
const NO_TILE_FOUND: Vector2i = Vector2i(-999999, -999999)
#
# ground, sid = 0
const GROUND_GRASS: Vector2i = Vector2i(0, 0)
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)
#
# objects, sid = 1
# NI = not interactive
const OBJECT_NI_RANDOM_1: Vector2i = Vector2i(0, 0) # testing only, to be removed
const OBJECT_NI_RANDOM_2: Vector2i = Vector2i(1, 0) # testing only, to be removed
const OBJECT_NI_ROCK_1: Vector2i = Vector2i(2, 0)
#
# I = interactive
# boat
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(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)
const OBJECT_I_BOAT_PART_CHEST: Vector2i = Vector2i(0, 2)
const OBJECT_I_BOAT_PART_GEARS: Vector2i = Vector2i(1, 2)
const OBJECT_I_BOAT_PART_MEDIKIT: Vector2i = Vector2i(2, 2)
const OBJECT_I_BOAT_PART_PADDLE: Vector2i = Vector2i(3, 2)
const OBJECT_I_BOAT_PART_GAS_STOVE: Vector2i = Vector2i(4, 2)
# camp
const OBJECT_I_TENT: Vector2i = Vector2i(6, 2)
const OBJECT_I_FIREPIT_OFF: Vector2i = Vector2i(6, 1)
const OBJECT_I_FIREPIT_ON: Vector2i = Vector2i(7, 1)
# other
const OBJECT_I_EMPTY_BUSH: Vector2i = Vector2i(3, 0)
const OBJECT_I_FILLED_BUSH: Vector2i = Vector2i(3, 1)
const OBJECT_I_BERRY: Vector2i = Vector2i(0, 5)
const OBJECT_I_STICK: Vector2i = Vector2i(1, 5)
const OBJECT_I_TREE_FULL: Vector2i = Vector2i(4, 0)
const OBJECT_I_TREE_CUT: Vector2i = Vector2i(5, 0)
# collections
const OBJECT_COLLECTION_BERRY_SOURCE: Array[Vector2i] = [OBJECT_I_FILLED_BUSH, OBJECT_I_BERRY]
const OBJECT_COLLECTION_STICK_SOURCE: Array[Vector2i] = [OBJECT_I_TREE_FULL, OBJECT_I_STICK]
const OBJECT_COLLECTION_FIREPIT: Array[Vector2i] = [OBJECT_I_FIREPIT_OFF, OBJECT_I_FIREPIT_ON]
const OBJECT_COLLECTION_BOAT_PARTS: Array[Vector2i] = [ # @formatter:off
OBJECT_I_BOAT_PART_GENERIC,
OBJECT_I_BOAT_PART_ENGINE, OBJECT_I_BOAT_PART_FUEL, OBJECT_I_BOAT_PART_ANCHOR,
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]
const OBJECT_RANDOM_1: Vector2i = Vector2i(0, 0) # testing only, to be removed
const OBJECT_RANDOM_2: Vector2i = Vector2i(1, 0) # testing only, to be removed
#
# temperature, sid = 2
const TEMPERATURE_NORMAL: Vector2i = Vector2i(2, 0)
const TEMPERATURE_NORMAL: Vector2i = Vector2i(-1, -1)
const TEMPERATURE_COLD_1: Vector2i = Vector2i(0, 0)
const TEMPERATURE_COLD_2: Vector2i = Vector2i(1, 0)
#
const NAVIGATION_CHECKED: Vector2i = Vector2i(0, 1)
const NAVIGATION_TARGET: Vector2i = Vector2i(3, 1)
const NAVIGATION_CHOSEN: Vector2i = Vector2i(1, 1)
const NAVIGATION_FAILED: Vector2i = Vector2i(2, 1)
#
# player, sid = 3
const PLAYER_DOWN: Vector2i = Vector2i(0, 0)
const PLAYER_UP: Vector2i = Vector2i(1, 0)
const PLAYER_LEFT: Vector2i = Vector2i(2, 0)
const PLAYER_RIGHT: Vector2i = Vector2i(3, 0)
#
const PLAYER_COLLECTION: Array[Vector2i] = [PLAYER_DOWN, PLAYER_UP, PLAYER_LEFT, PLAYER_RIGHT]
func player_sprite_from_direction(direction: Vector2i) -> Vector2i:
if direction == Vector2i(0, 1):
return PLAYER_DOWN
if direction == Vector2i(0, -1):
return PLAYER_UP
if direction == Vector2i(-1, 0):
return PLAYER_LEFT
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
const PLAYER: Vector2i = Vector2i(0, 0)

View File

@ -1,200 +0,0 @@
class_name TilemapNavigation
extends Node
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
#
var world: World = null
var player: PlayerManager = null
#
# Dictionary[Vector2i, Array[Vector2i]] (target, path)
var found_paths: Dictionary = {}
var failed_positions: Array[Vector2i] = []
var chosen_path: Array[Vector2i] = []
func game_tick_start() -> void:
found_paths.clear()
failed_positions = []
chosen_path = []
func game_tick_end() -> void:
world.tilemap_nav_vis.clear_cells()
for path in found_paths.values():
for pos in path:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_CHECKED)
for pos in failed_positions:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_FAILED)
for pos in chosen_path:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_CHOSEN)
# mark last in chosen path as NAVIGATION_TARGET
if chosen_path.size() > 0:
world.tilemap_nav_vis.set_cell(chosen_path[chosen_path.size() - 1], tilemap_types.NAVIGATION_TARGET)
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:
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:
var closest: Vector2i = tilemap_types.NO_TILE_FOUND
var shortest: int = 9999999999
for option in options:
var distance: int = manhattan_distance(option, target)
if distance < shortest:
closest = option
shortest = distance
return closest
var walking_directions: Array[Vector2i] = [Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0)]
var f_score: Dictionary = {}
func find_path_allow_neighbors(start_position: Vector2i, end_position: Vector2i, max_radius: int = -1) -> Array[Vector2i]:
if world.is_walkable(end_position):
# check the tile itself first, then check the four surrounding tiles
var path: Array[Vector2i] = find_path(start_position, end_position, max_radius)
if path.size() != 0:
return path
else:
# be smart about which to check first
var directions: Array[Vector2i] = []
if start_position.y < end_position.y:
directions = [Vector2i(0, -1), Vector2i(-1, 0), Vector2i(1, 0), Vector2i(0, 1)]
elif start_position.y > end_position.y:
directions = [Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0), Vector2i(0, -1)]
elif start_position.x < end_position.x:
directions = [Vector2i(-1, 0), Vector2i(0, -1), Vector2i(0, 1), Vector2i(1, 0)]
elif start_position.x > end_position.x:
directions = [Vector2i(1, 0), Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0)]
else:
directions = walking_directions
for direction in directions:
var neighbor: Vector2i = end_position + direction
var path: Array[Vector2i] = find_path(start_position, neighbor, max_radius)
if path.size() != 0:
return path
return []
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:
found_paths[end_position] = path
else:
failed_positions.append(end_position)
return path
func _find_path_internal(start_position: Vector2i, end_position: Vector2i, max_radius: int = -1) -> Array[Vector2i]:
if max_radius > -1 and not is_within_radius(end_position, start_position, max_radius):
return []
if not world.is_walkable(end_position):
return []
var check_nodes = PriorityQueue.new() # lowest f_score
var came_from: Dictionary = {}
var g_score: Dictionary = {}
var walkable_cache: Dictionary = {}
f_score = {}
var visited_nodes: Dictionary = {}
check_nodes.insert(start_position, 0)
g_score[start_position] = 0
f_score[start_position] = manhattan_distance(start_position, end_position) * 1.1 # Heuristic weighting
while not check_nodes.empty():
var current: Vector2i = check_nodes.extract()
if current == end_position:
var path: Array[Vector2i] = []
while current in came_from:
path.insert(0, current)
current = came_from[current]
path.insert(0, start_position)
return path
visited_nodes[current] = true
for direction in walking_directions:
var neighbor: Vector2i = current + direction
# Combine checks for early skipping
if neighbor in visited_nodes or (max_radius > -1 and not is_within_radius(neighbor, start_position, max_radius)):
continue
if not walkable_cache.has(neighbor):
walkable_cache[neighbor] = world.is_walkable(neighbor)
if not walkable_cache[neighbor]:
continue
var cost: int = world.tilemap_ground.get_custom_data(neighbor, "cost", 1)
var tentative_g_score: int = g_score.get(current, INF) + cost
if tentative_g_score < g_score.get(neighbor, INF):
came_from[neighbor] = current
g_score[neighbor] = tentative_g_score
f_score[neighbor] = tentative_g_score + manhattan_distance(neighbor, end_position) * 1.1 # Heuristic weighting
if not check_nodes.contains(neighbor):
check_nodes.insert(neighbor, f_score[neighbor])
return []

View File

@ -1,19 +1,13 @@
class_name World
extends Node2D
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
# tilemap layers
var tilemap_ground: TileMapLayerAccess = TileMapLayerAccess.new()
var tilemap_non_interactive: TileMapLayerAccess = TileMapLayerAccess.new()
var tilemap_interactive: TileMapLayerAccess = TileMapLayerAccess.new()
var tilemap_player: TileMapLayerAccess = TileMapLayerAccess.new()
var tilemap_temperature: TileMapLayerAccess = TileMapLayerAccess.new()
var tilemap_nav_vis: TileMapLayerAccess = TileMapLayerAccess.new()
# managers
var camp_manager: CampManager = CampManager.new()
# visualization
@onready var step_visualizer: StepVisualization = $StepVisualization
#
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
func _ready() -> void:
@ -27,8 +21,6 @@ func _ready() -> void:
tilemap_player.tilemap = $PlayerLayer
tilemap_temperature.sid = 2
tilemap_temperature.tilemap = $TemperatureLayer
tilemap_nav_vis.sid = 2
tilemap_nav_vis.tilemap = $NavigationVisualization
tilemap_ground.setup()
tilemap_non_interactive.setup()
@ -36,12 +28,6 @@ func _ready() -> void:
tilemap_player.setup()
tilemap_temperature.setup()
call_deferred("defer_ready")
func defer_ready() -> void:
camp_manager.setup()
# example usage
# tilemap_temperature.fill_area(Vector2i(0, 0), Vector2i(10, 10), tilemap_types.TEMPERATURE_COLD_1)
@ -49,56 +35,9 @@ func defer_ready() -> void:
# print(tilemap_non_interactive.get_cells_by_custom_data("walkable", true))
# tilemap_ground.clear_cells()
# tilemap_ground.set_cell(Vector2i(0, 0), tilemap_types.GROUND_GRASS)
# print(tilemap_ground.local_to_cell(get_local_mouse_position()))
func tilemap_mouse_position() -> Vector2i:
return tilemap_ground.local_to_cell(get_local_mouse_position())
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)
if not tilemap_interactive.get_cell(check_pos) and is_walkable(check_pos):
return check_pos
return Vector2i(-1, -1)
func _process(delta: float) -> void:
pass
func is_walkable(position: Vector2i) -> bool:
var ground_tile_walkable: bool = tilemap_ground.get_custom_data(position, "walkable", false)
var non_interactive_walkable: bool = tilemap_non_interactive.get_custom_data(position, "walkable", true)
var interactive_walkable: bool = tilemap_interactive.get_custom_data(position, "walkable", true)
return ground_tile_walkable and non_interactive_walkable and interactive_walkable
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)
for bush in empty_bushes:
if randf() < 0.01:
tilemap_interactive.set_cell(bush, tilemap_types.OBJECT_I_FILLED_BUSH)
# refill empty trees
var empty_trees: Array[Vector2i] = tilemap_interactive.get_cells_by_type(tilemap_types.OBJECT_I_TREE_CUT)
for tree in empty_trees:
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()
# print(tilemap_ground.local_to_cell(get_local_mouse_position()))

View File

@ -1,184 +0,0 @@
class_name CampManager
extends Node
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
#
var game_manager: GameManager = null
var tilemap_interactive: TileMapLayerAccess = null
#
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:
tilemap_interactive = game_manager.world.tilemap_interactive
var camp_locations: Array[Vector2i] = tilemap_interactive.get_cells_by_type(tilemap_types.OBJECT_I_TENT)
if len(camp_locations) > 0:
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")
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 camp_items.find(item) != -1
func camp_contains_item_collection(item: Array[Vector2i]) -> bool:
for i in item:
if camp_items.find(i) == -1:
return false
return true
func camp_item_count(item: Vector2i) -> int:
var count: int = 0
for i in camp_items:
if i == item:
count += 1
return count
func camp_item_collection_count(item: Array[Vector2i]) -> int:
var count: int = 0
for i in camp_items:
if item.find(i) != -1:
count += 1
return count
func camp_take_item(item: Vector2i, count: int = 1) -> bool:
if camp_item_count(item) < count:
push_error("CampManager: not enough items to take: " + str(item))
EventsTracker.track(EventsTracker.Event.CAMP_TAKE_ITEM_FAILED, {"item": item, "count": count})
return false
var taken: int = 0
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
EventsTracker.track(EventsTracker.Event.CAMP_TAKEN_ITEM, {"item": item, "count": count})
return true
func camp_add_item(item: Vector2i) -> void:
camp_items.append(item)
EventsTracker.track(EventsTracker.Event.CAMP_ADDED_ITEM, {"item": item, "count": 1, "new_count": camp_item_count(item)})
func campfire_light() -> bool:
# requires two sticks in the camp
if camp_take_item(tilemap_types.OBJECT_I_STICK, 2):
tilemap_interactive.set_cell(campfire, tilemap_types.OBJECT_I_FIREPIT_ON)
EventsTracker.track(EventsTracker.Event.CAMPFIRE_LIT)
return true
else:
EventsTracker.track(EventsTracker.Event.CAMPFIRE_LIT_FAILED)
return false
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

View File

@ -1,29 +0,0 @@
class_name PerformanceTimer
extends Node
var start_time: float = 0.0
var accumulated_time: float = 0.0
var display_name: String = ""
func _init() -> void:
start_time = Time.get_unix_time_from_system()
display_name = ""
func start() -> void:
start_time = Time.get_unix_time_from_system()
func stop(printme: bool = true) -> void:
var end_time: float = Time.get_unix_time_from_system()
var duration_ms: float = (end_time - start_time) * 1000
accumulated_time += duration_ms
if printme:
printme()
func printme() -> void:
print(display_name, " ", accumulated_time, "ms")
func per_count(n: String, count: int) -> void:
print(display_name, " ", accumulated_time / count, "ms per ", n, " (", count, ")")

View File

@ -1,71 +0,0 @@
class_name PriorityQueue
var _data: Array = []
var _set: Dictionary = {}
func insert(element: Vector2, cost: float) -> void:
if element in _set:
return
# Add the element to the bottom level of the heap at the leftmost open space
self._data.push_back(Vector3(element.x, element.y, cost))
self._set[element] = true
var new_element_index: int = self._data.size() - 1
self._up_heap(new_element_index)
func extract():
if self.empty():
return null
var result: Vector3 = self._data.pop_front()
_set.erase(Vector2(result.x, result.y))
# If the tree is not empty, replace the root of the heap with the last
# element on the last level.
if not self.empty():
self._data.push_front(self._data.pop_back())
self._down_heap(0)
return Vector2(result.x, result.y)
func contains(element: Vector2) -> bool:
return element in _set
func empty() -> bool:
return self._data.size() == 0
func _get_parent(index: int) -> int:
# warning-ignore:integer_division
return (index - 1) / 2
func _left_child(index: int) -> int:
return (2 * index) + 1
func _right_child(index: int) -> int:
return (2 * index) + 2
func _swap(a_idx: int, b_idx: int) -> void:
var a = self._data[a_idx]
var b = self._data[b_idx]
self._data[a_idx] = b
self._data[b_idx] = a
func _up_heap(index: int) -> void:
# Compare the added element with its parent; if they are in the correct order, stop.
var parent_idx = self._get_parent(index)
if self._data[index].z >= self._data[parent_idx].z:
return
self._swap(index, parent_idx)
self._up_heap(parent_idx)
func _down_heap(index: int) -> void:
var left_idx: int = self._left_child(index)
var right_idx: int = self._right_child(index)
var smallest: int = index
var size: int = self._data.size()
if right_idx < size and self._data[right_idx].z < self._data[smallest].z:
smallest = right_idx
if left_idx < size and self._data[left_idx].z < self._data[smallest].z:
smallest = left_idx
if smallest != index:
self._swap(index, smallest)
self._down_heap(smallest)

Some files were not shown because too many files have changed in this diff Show More