Compare commits
82 Commits
Author | SHA1 | Date |
---|---|---|
|
3b4d8a4d8e | |
|
3d716ed06d | |
|
f3f4cef1bf | |
|
263816fa53 | |
|
08647a4188 | |
|
7a18765ebd | |
|
da1fdf10d0 | |
|
624c14150e | |
|
a75eb4aa1d | |
|
28c9b857bd | |
|
2e1559d96e | |
|
9822c716f2 | |
|
b187b96507 | |
|
fcbd269f68 | |
|
b3220f968d | |
|
dc3ead30b4 | |
|
d026c07804 | |
|
4b8479e87e | |
|
d8bc800077 | |
|
0becddf3d2 | |
|
cd01fd24a2 | |
|
57db80e685 | |
|
268f2c0ce1 | |
|
4763de9827 | |
|
238af1cc83 | |
|
cd82cf5bdf | |
|
2923738906 | |
|
8bbca6cef5 | |
|
7304b48f3c | |
|
9446c10556 | |
|
731ff9ed12 | |
|
f27f1b944a | |
|
ed665886d3 | |
|
75af4679c0 | |
|
947b762774 | |
|
7d5f91adcb | |
|
5c0fc2beed | |
|
e36bbd36dc | |
|
fe7db59dd8 | |
|
30ee500ae9 | |
|
e280b7dc72 | |
|
658c5811dd | |
|
09bcacf75e | |
|
162812b9c7 | |
|
a4ee7cc336 | |
|
deb896bf17 | |
|
fc1795529e | |
|
46ad66380d | |
|
798c3c4fa1 | |
|
93e3a26b24 | |
|
b89131bbaa | |
|
3c61ce4e7f | |
|
bfca1ff23f | |
|
b3e33b7cfa | |
|
51a04ca403 | |
|
22fa9eb33e | |
|
e1c87b5e4d | |
|
b49d3a6d27 | |
|
aee610c7a0 | |
|
092203a31e | |
|
1eebbfe264 | |
|
ad3023cc44 | |
|
996ec1a297 | |
|
b5b016d5d6 | |
|
e8f9a0b163 | |
|
fb84f548d4 | |
|
d81f9cace8 | |
|
37f198bc95 | |
|
facdb2487a | |
|
cfab98a8d0 | |
|
2d03502722 | |
|
83c815937f | |
|
808efedd5e | |
|
027257fcb6 | |
|
567b2cb689 | |
|
208dfd20d5 | |
|
cb9cc1f8b1 | |
|
42b8f56c2b | |
|
e9fba43aec | |
|
db933cd620 | |
|
9d9f29dbcf | |
|
64106039c8 |
|
@ -1,2 +1,3 @@
|
|||
project/.godot
|
||||
.idea
|
||||
.vscode
|
||||
|
|
|
@ -1,4 +1,34 @@
|
|||
- Sprites (dome)
|
||||
## 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
|
||||
- Initialize Tilemap (Yan)
|
||||
- Script --> World (manages access to tilemap)
|
||||
- Player is on tilemap
|
||||
|
@ -7,25 +37,24 @@
|
|||
- 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)
|
||||
- Design a tilemap for the game
|
||||
- Player (Dome)
|
||||
- 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
|
||||
- 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 (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
|
||||
- ...
|
||||
- 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)
|
||||
|
|
After Width: | Height: | Size: 474 KiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://di16cmfomo60u"
|
||||
path="res://.godot/imported/intro.png-5733d5421d999e0273f5c7e8c62d2491.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/intro.png"
|
||||
dest_files=["res://.godot/imported/intro.png-5733d5421d999e0273f5c7e8c62d2491.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
After Width: | Height: | Size: 31 KiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cr4e1faypphkg"
|
||||
path="res://.godot/imported/ingame-instructions.png-47b6e281450dc0b79c6fd2f40f059336.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/ingame-instructions.png"
|
||||
dest_files=["res://.godot/imported/ingame-instructions.png-47b6e281450dc0b79c6fd2f40f059336.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
After Width: | Height: | Size: 1.4 MiB |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cynull1bsik42"
|
||||
path="res://.godot/imported/outro.png-474f491bb8e2fbb9b182fa83ab072d54.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/images/outro.png"
|
||||
dest_files=["res://.godot/imported/outro.png-474f491bb8e2fbb9b182fa83ab072d54.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
|
@ -0,0 +1,17 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[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)
|
|
@ -1,48 +1 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
{"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}
|
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 3.6 KiB |
|
@ -2,7 +2,7 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dy0gpc2vgr3o5"
|
||||
uid="uid://diwoxcyj13q7v"
|
||||
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=1
|
||||
detect_3d/compress_to=0
|
||||
|
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 5.5 KiB |
|
@ -2,7 +2,7 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cvb8hqljk0rv3"
|
||||
uid="uid://dkvyu6a2bqans"
|
||||
path="res://.godot/imported/tilemap_objects.png-36dc04b2c5c4ea8db297745921fe10e8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
|
|
Before Width: | Height: | Size: 132 B After Width: | Height: | Size: 1.6 KiB |
|
@ -2,7 +2,7 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bam5su5j62mof"
|
||||
uid="uid://bwkvdumh22cmh"
|
||||
path="res://.godot/imported/tilemap_player.png-ea1fa2aedf5bb220961e9080aa573e32.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
|
|
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 201 B |
|
@ -2,7 +2,7 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d1sdhqjtrt1ng"
|
||||
uid="uid://dtoy4tdohey8t"
|
||||
path="res://.godot/imported/tilemap_temperature.png-df4acfda23c8781105475512aa320086.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
|
|
|
@ -1,41 +1,125 @@
|
|||
[gd_resource type="TileSet" load_steps=9 format=3 uid="uid://bi836ygcmyvhb"]
|
||||
|
||||
[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"]
|
||||
[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"]
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_114re"]
|
||||
texture = ExtResource("1_ukrsa")
|
||||
0:0/0 = 0
|
||||
0:0/0/custom_data_0 = true
|
||||
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_7jeam"]
|
||||
texture = ExtResource("2_o4fdg")
|
||||
[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
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_1og8x"]
|
||||
texture = ExtResource("3_xap0v")
|
||||
0:0/0 = 0
|
||||
0:0/0/custom_data_1 = 100
|
||||
0:0/0/custom_data_1 = 10
|
||||
1:0/0 = 0
|
||||
1:0/0/custom_data_1 = -200
|
||||
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
|
||||
|
||||
[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")
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,269 +0,0 @@
|
|||
[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")
|
|
@ -66,13 +66,65 @@ 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)
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
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
|
|
@ -1,12 +1,14 @@
|
|||
class_name Camera
|
||||
class_name CameraController
|
||||
extends Camera2D
|
||||
|
||||
@export var border_acceleration: float = 800.0
|
||||
@onready var shader_vignette: ColorRect = $CanvasLayer/Vignette
|
||||
|
||||
@export var border_acceleration: float = 2000.0
|
||||
@export var max_speed: float = 500.0
|
||||
@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)
|
||||
@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)
|
||||
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
#
|
||||
|
@ -29,7 +31,11 @@ func go_to(position: Vector2) -> void:
|
|||
|
||||
|
||||
func print_config() -> void:
|
||||
print("camera.go_to_zooming(Vector2(", position.x, ", ", position.y, "), ", zoom, ")")
|
||||
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)
|
||||
|
||||
|
||||
func _input(event):
|
||||
|
@ -70,9 +76,9 @@ func _process(delta):
|
|||
var is_zoom_out: bool = Input.is_action_pressed("camera_zoom_out")
|
||||
|
||||
if is_zoom_in:
|
||||
zoom = zoom * 1.1
|
||||
zoom = zoom * 1.02
|
||||
elif is_zoom_out:
|
||||
zoom = zoom / 1.1
|
||||
zoom = zoom / 1.02
|
||||
|
||||
if zoom.length() < 0.2:
|
||||
zoom = Vector2(1, 1).normalized() * 0.2
|
||||
|
@ -83,10 +89,13 @@ func _process(delta):
|
|||
var screen_size: Vector2 = get_viewport().get_visible_rect().size
|
||||
var acceleration: Vector2 = Vector2.ZERO
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
if is_left:
|
||||
acceleration.x = -border_acceleration
|
||||
|
@ -118,3 +127,10 @@ 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)
|
|
@ -0,0 +1,112 @@
|
|||
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)
|
|
@ -1,17 +1,295 @@
|
|||
class_name GameManager
|
||||
extends Node
|
||||
|
||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
|
||||
@onready var world: World = $Tileset
|
||||
@onready var player: PlayerManager = $PlayerManager
|
||||
@onready var camera: Camera2D = $Camera2D
|
||||
@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
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
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)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
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()
|
||||
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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class_name TrackedEvent
|
||||
extends Node
|
||||
|
||||
var event: EventsTracker.Event = -1
|
||||
var params: Dictionary = {}
|
|
@ -1,2 +1,248 @@
|
|||
class_name PlayerManager
|
||||
extends Node
|
||||
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()
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
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)
|
|
@ -0,0 +1,133 @@
|
|||
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
|
|
@ -0,0 +1,28 @@
|
|||
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)
|
|
@ -0,0 +1,28 @@
|
|||
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)
|
|
@ -0,0 +1,28 @@
|
|||
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)
|
|
@ -0,0 +1,23 @@
|
|||
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"
|
|
@ -0,0 +1,23 @@
|
|||
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"
|
|
@ -0,0 +1,114 @@
|
|||
class_name TaskPlannedExploration
|
||||
extends Task
|
||||
|
||||
var last_goals: Array[Vector2i] = []
|
||||
var current_goal: Vector2i = tilemap_types.NO_TILE_FOUND
|
||||
var closest_distance_to_goal: float = 99999999
|
||||
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
# check if player distance is < 10 to the camp (world, camp_manager, camp) and if he was closer than the view distance to the goal once (closest_distance_to_goal), reset the goal in that case
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, world.camp_manager.camp) < 10 and closest_distance_to_goal < player.view_distance:
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
closest_distance_to_goal = 99999999
|
||||
EventsTracker.track(EventsTracker.Event.EXPLORATION_GOAL_CLOSE_ENOUGH, {"item": tilemap_types.OBJECT_I_TENT})
|
||||
print("Resetting goal, player close to camp and was close to goal once")
|
||||
|
||||
# check if player distance is < 2 to the current goal
|
||||
if current_goal != tilemap_types.NO_TILE_FOUND:
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, current_goal) < 3:
|
||||
EventsTracker.track(EventsTracker.Event.EXPLORATION_GOAL_REACHED, {"goal": current_goal})
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
|
||||
if current_goal == tilemap_types.NO_TILE_FOUND:
|
||||
find_new_goal(world, player, navigation)
|
||||
if current_goal != tilemap_types.NO_TILE_FOUND:
|
||||
EventsTracker.track(EventsTracker.Event.NEW_EXPLORATION_GOAL, {"goal": current_goal})
|
||||
closest_distance_to_goal = 99999999
|
||||
|
||||
if current_goal == tilemap_types.NO_TILE_FOUND:
|
||||
status = Task.FAILURE
|
||||
status_reason = "No goal found"
|
||||
return
|
||||
|
||||
StepVisualization.add_circle_tileset(current_goal, 2, StepVisualization.CircleType.GOAL)
|
||||
StepVisualization.add_line_tileset(player.board_position, current_goal, StepVisualization.LineType.SEARCH_SELECTED)
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "exploration_goal", current_goal)
|
||||
if path.size() == 0:
|
||||
current_goal = tilemap_types.NO_TILE_FOUND
|
||||
status = Task.FAILURE
|
||||
status_reason = "No path found"
|
||||
return
|
||||
|
||||
blackboard["path"] = path
|
||||
|
||||
status = Task.SUCCESS
|
||||
status_reason = "goal: " + str(current_goal)
|
||||
|
||||
|
||||
func find_new_goal(world: World, player: PlayerManager, navigation: TilemapNavigation) -> void:
|
||||
if last_goals.size() == 0:
|
||||
last_goals.append(world.camp_manager.camp)
|
||||
|
||||
# perform search for a new goal X times, pick the one that is furthest away from the last goal
|
||||
var best_goal: Vector2i = tilemap_types.NO_TILE_FOUND
|
||||
var best_distance: float = 0
|
||||
|
||||
for i in range(3):
|
||||
var goal_consideration: Vector2i = determine_an_interesting_goal(world)
|
||||
if goal_consideration == tilemap_types.NO_TILE_FOUND:
|
||||
continue
|
||||
StepVisualization.add_circle_tileset(goal_consideration, 2, StepVisualization.CircleType.GOAL_CONSIDERATION)
|
||||
StepVisualization.add_line_tileset(player.board_position, goal_consideration, StepVisualization.LineType.SEARCH_BASE)
|
||||
|
||||
var distance: float = TilemapNavigation.manhattan_distance(goal_consideration, navigation.manhattan_distance_closest(last_goals, goal_consideration))
|
||||
if distance > best_distance:
|
||||
best_goal = goal_consideration
|
||||
best_distance = distance
|
||||
|
||||
if best_goal != tilemap_types.NO_TILE_FOUND:
|
||||
last_goals.append(best_goal)
|
||||
current_goal = best_goal
|
||||
|
||||
|
||||
func determine_an_interesting_goal(world: World) -> Vector2i:
|
||||
# starting from the camp position (world.camp_manager.camp),
|
||||
# pick a random direction (360 degrees, random on x and y then normalize),
|
||||
# then step in that direction until the last walkable tile is found
|
||||
# (if not walkable, check every step for the next 10 tiles again and only stop if none of them are walkable)
|
||||
# then, pick a random walkable tile in the area around that last walkable tile,
|
||||
# and check if the player can get there using the navigation system.
|
||||
|
||||
var camp_position: Vector2 = Vector2(world.camp_manager.camp)
|
||||
var direction: Vector2 = Vector2(randf() * 2 - 1, randf() * 2 - 1).normalized()
|
||||
|
||||
var last_walkable: Vector2i = Vector2i(0, 0)
|
||||
var iterations_no_walkable: int = 0
|
||||
|
||||
for i in range(2500):
|
||||
var check_position: Vector2i = camp_position + (direction * i * 2).floor()
|
||||
if not world.is_walkable(check_position):
|
||||
iterations_no_walkable += 1
|
||||
else:
|
||||
iterations_no_walkable = 0
|
||||
last_walkable = check_position
|
||||
|
||||
if iterations_no_walkable > 10:
|
||||
break
|
||||
|
||||
if last_walkable == Vector2i(0, 0):
|
||||
return tilemap_types.NO_TILE_FOUND
|
||||
|
||||
var picked_goal: Vector2i = Vector2i(0, 0)
|
||||
for i in range(10):
|
||||
var check_position: Vector2i = last_walkable + Vector2i(randi_range(-10, 10), randi_range(-10, 10))
|
||||
if world.is_walkable(check_position) and world.tilemap_ground.get_custom_data(check_position, "cost", 999) < 7:
|
||||
picked_goal = check_position
|
||||
break
|
||||
|
||||
return picked_goal
|
|
@ -0,0 +1,14 @@
|
|||
class_name TaskRandomWalking
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var direction: Vector2i = navigation.walking_directions[randi() % navigation.walking_directions.size()]
|
||||
var target: Vector2i = player.board_position + direction
|
||||
|
||||
player.walk_towards(target)
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Walking towards " + str(target)
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCheckFoodBaseThreshold
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if player.food > player.food_base_threshold:
|
||||
status = FAILURE
|
||||
status_reason = "Player food is not base threshold (" + str(player.food) + " > " + str(player.food_critical_threshold) + ")"
|
||||
return
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Player food is base threshold (" + str(player.food) + " <= " + str(player.food_critical_threshold) + ")"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCheckBoatCompleted
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.boat_items.size() >= world.camp_manager.required_boat_parts:
|
||||
status = SUCCESS
|
||||
status_reason = "Boat is completed with " + str(world.camp_manager.boat_items.size()) + " parts"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Boat is not completed, got only " + str(world.camp_manager.boat_items.size()) + " parts"
|
|
@ -0,0 +1,20 @@
|
|||
class_name TaskDeliverBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
EventsTracker.track(EventsTracker.Event.CAMP_BOAT_PART_DELIVERED, {"item": player.inventory_slot})
|
||||
world.camp_manager.boat_items.append(player.inventory_slot)
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
if world.camp_manager.boat_items.size() >= world.camp_manager.required_boat_parts:
|
||||
EventsTracker.track(EventsTracker.Event.CAMP_BOAT_COMPLETE, {"item": tilemap_types.OBJECT_I_BOAT_WITH_ENGINE})
|
||||
world.tilemap_interactive.set_cell(world.camp_manager.boat_build_location, tilemap_types.OBJECT_I_BOAT_WITH_ENGINE)
|
||||
status = SUCCESS
|
||||
status_reason = "Player delivered boat part"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat part to deliver"
|
|
@ -0,0 +1,31 @@
|
|||
class_name TaskFindClosestBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
status = FAILURE
|
||||
status_reason = "Player already has boat part"
|
||||
return
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, "boat_part")
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
var closest_part: Vector2i = result.closest_item
|
||||
blackboard["closest_part"] = closest_part
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat_part", closest_part, player.view_distance * 1.5)
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to closest boat part"
|
||||
status = SUCCESS
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "No path found to closest boat part " + str(closest_part)
|
|
@ -0,0 +1,17 @@
|
|||
class_name TaskGoToBoatLeaveLocation
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var target: Vector2i = world.camp_manager.boat_leave_location
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat_leave", target)
|
||||
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to boat leave location"
|
||||
status = SUCCESS
|
||||
else:
|
||||
status = FAILURE
|
||||
status_reason = "No path found to boat leave location " + str(target)
|
|
@ -0,0 +1,25 @@
|
|||
class_name TaskGoToBoatLocation
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT, "boat_building_location", TileMapLayerAccess.ANY_DISTANCE)
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
# var target: Vector2i = world.camp_manager.boat_build_location
|
||||
var target: Vector2i = result.closest_item
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_boat", target)
|
||||
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status_reason = "Found path to boat build location"
|
||||
status = SUCCESS
|
||||
else:
|
||||
status = FAILURE
|
||||
status_reason = "No path found to boat build location " + str(target)
|
|
@ -0,0 +1,12 @@
|
|||
class_name TaskWinningSequence
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
world.tilemap_interactive.set_cell(world.camp_manager.boat_leave_location, player.inventory_slot)
|
||||
EventsTracker.track(EventsTracker.Event.PLAYER_USED_ITEM, {"item": player.inventory_slot})
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
|
||||
blackboard["game_state_win"] = true
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskCampContainsEnoughSticksToLightCampfire
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.camp_contains_enough_sticks_to_light_campfire():
|
||||
status = SUCCESS
|
||||
status_reason = "Camp contains enough sticks to light campfire"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Camp does not contain enough sticks to light campfire"
|
|
@ -0,0 +1,24 @@
|
|||
class_name TaskCampSleep
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if not world.camp_manager.is_sleep_active and world.camp_manager.is_sundown():
|
||||
world.camp_manager.campfire_light()
|
||||
world.camp_manager.sleep_effect()
|
||||
player.health = player.max_health
|
||||
status = RUNNING
|
||||
status_reason = "Sleeping"
|
||||
return
|
||||
|
||||
if world.camp_manager.is_sleep_active:
|
||||
player.food += 1
|
||||
status = RUNNING
|
||||
status_reason = "Still sleeping"
|
||||
return
|
||||
|
||||
world.camp_manager.campfire_extinguish()
|
||||
status = SUCCESS
|
||||
status_reason = "Slept"
|
|
@ -0,0 +1,19 @@
|
|||
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"])
|
|
@ -0,0 +1,25 @@
|
|||
class_name TaskGoCloseToCamp
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
var navigation: TilemapNavigation = blackboard["navigation"]
|
||||
|
||||
# allow a radius of the player view distance / 1.5 to find a camp
|
||||
var camp: Vector2i = world.camp_manager.camp
|
||||
if TilemapNavigation.manhattan_distance(player.board_position, camp) < player.view_distance / 1.5:
|
||||
blackboard["path"] = []
|
||||
status = SUCCESS
|
||||
status_reason = "Player is close to camp"
|
||||
return
|
||||
|
||||
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "path_to_camp", camp, 99999999)
|
||||
if path.size() > 0:
|
||||
blackboard["path"] = path
|
||||
status = FAILURE
|
||||
status_reason = "Found path to camp"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "No path found to camp " + str(camp)
|
|
@ -0,0 +1,16 @@
|
|||
class_name TaskPutInventoryContentInCamp
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if player.inventory_slot != tilemap_types.EMPTY:
|
||||
world.camp_manager.camp_add_item(player.inventory_slot)
|
||||
player.inventory_slot = tilemap_types.EMPTY
|
||||
status = SUCCESS
|
||||
status_reason = "Put inventory content in camp"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player has no inventory content"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskWantsToSleep
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var world: World = blackboard["world"]
|
||||
|
||||
if world.camp_manager.is_sundown():
|
||||
status = SUCCESS
|
||||
status_reason = "It is sundown " + str(world.camp_manager.time_of_day)
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "It is not sundown " + str(world.camp_manager.time_of_day)
|
|
@ -0,0 +1,15 @@
|
|||
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) + ")"
|
|
@ -0,0 +1,27 @@
|
|||
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)
|
|
@ -0,0 +1,13 @@
|
|||
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"
|
|
@ -0,0 +1,33 @@
|
|||
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)
|
|
@ -0,0 +1,33 @@
|
|||
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)
|
|
@ -0,0 +1,35 @@
|
|||
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)
|
|
@ -0,0 +1,17 @@
|
|||
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"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsBoat
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT, player.inventory_slot):
|
||||
status = SUCCESS
|
||||
status_reason = "Player has boat"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if tilemap_types.is_part_of_collection(tilemap_types.OBJECT_COLLECTION_BOAT_PARTS, player.inventory_slot):
|
||||
status = SUCCESS
|
||||
status_reason = "Player has boat part"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have boat part"
|
|
@ -0,0 +1,13 @@
|
|||
class_name TaskInventoryContainsStick
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
if player.inventory_slot == tilemap_types.OBJECT_I_STICK:
|
||||
status = SUCCESS
|
||||
status_reason = "Player has stick"
|
||||
return
|
||||
|
||||
status = FAILURE
|
||||
status_reason = "Player does not have stick"
|
|
@ -0,0 +1,16 @@
|
|||
class_name TaskPickupBoat
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
|
||||
var result: Dictionary = find_closest_item(blackboard, tilemap_types.OBJECT_COLLECTION_BOAT, "boat_building_location", TileMapLayerAccess.ANY_DISTANCE)
|
||||
|
||||
if result.status == FAILURE:
|
||||
status = FAILURE
|
||||
status_reason = result.status_reason
|
||||
return
|
||||
|
||||
player.pick_up_item(result.closest_item)
|
||||
status = SUCCESS
|
||||
status_reason = "Picked up boat"
|
|
@ -0,0 +1,12 @@
|
|||
class_name TaskPickupBoatPart
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var player: PlayerManager = blackboard["player"]
|
||||
var closest_part: Vector2i = blackboard["closest_part"]
|
||||
|
||||
player.pick_up_item(closest_part)
|
||||
player.player_memory.erase("boat_part")
|
||||
|
||||
status = SUCCESS
|
||||
status_reason = "Picked up boat part"
|
|
@ -0,0 +1,11 @@
|
|||
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"
|
|
@ -0,0 +1,11 @@
|
|||
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"
|
|
@ -0,0 +1,16 @@
|
|||
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)
|
|
@ -0,0 +1,9 @@
|
|||
class_name Task5050Running
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var random: int = randi() % 2
|
||||
if random == 0:
|
||||
status = RUNNING
|
||||
else:
|
||||
status = SUCCESS
|
|
@ -0,0 +1,9 @@
|
|||
class_name Task5050Success
|
||||
extends Task
|
||||
|
||||
func run(blackboard: Dictionary) -> void:
|
||||
var random: int = randi() % 2
|
||||
if random == 0:
|
||||
status = SUCCESS
|
||||
else:
|
||||
status = FAILURE
|
|
@ -0,0 +1,14 @@
|
|||
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
|
|
@ -0,0 +1,15 @@
|
|||
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
|
|
@ -0,0 +1,78 @@
|
|||
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)
|
|
@ -1,6 +1,8 @@
|
|||
class_name TileMapLayerAccess
|
||||
extends Node
|
||||
|
||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
#
|
||||
var tilemap: TileMapLayer = null
|
||||
var sid: int = 0
|
||||
|
||||
|
@ -9,8 +11,38 @@ func setup() -> void:
|
|||
pass
|
||||
|
||||
|
||||
func get_cells_by_type(atlas_coords: Vector2i) -> Array[Vector2i]:
|
||||
return tilemap.get_used_cells_by_id(sid, atlas_coords)
|
||||
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_custom_data(field_name: String, custom_data: Variant) -> Array[Vector2i]:
|
||||
|
@ -22,8 +54,10 @@ 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) -> Variant:
|
||||
func get_custom_data(coords: Vector2i, field_name: String, default_value: Variant) -> 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)
|
||||
|
||||
|
||||
|
@ -38,6 +72,12 @@ 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)
|
||||
|
||||
|
@ -77,3 +117,11 @@ 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)
|
||||
|
|
|
@ -1,19 +1,88 @@
|
|||
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
|
||||
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
|
||||
# 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]
|
||||
#
|
||||
# temperature, sid = 2
|
||||
const TEMPERATURE_NORMAL: Vector2i = Vector2i(-1, -1)
|
||||
const TEMPERATURE_NORMAL: Vector2i = Vector2i(2, 0)
|
||||
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: Vector2i = Vector2i(0, 0)
|
||||
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
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
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 []
|
|
@ -1,13 +1,19 @@
|
|||
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_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
var tilemap_nav_vis: TileMapLayerAccess = TileMapLayerAccess.new()
|
||||
# managers
|
||||
var camp_manager: CampManager = CampManager.new()
|
||||
|
||||
# visualization
|
||||
@onready var step_visualizer: StepVisualization = $StepVisualization
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -21,6 +27,8 @@ 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()
|
||||
|
@ -28,6 +36,12 @@ 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)
|
||||
|
@ -35,9 +49,56 @@ func _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 _process(delta: float) -> void:
|
||||
pass
|
||||
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)
|
||||
|
||||
# print(tilemap_ground.local_to_cell(get_local_mouse_position()))
|
||||
|
||||
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()
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
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
|
|
@ -0,0 +1,29 @@
|
|||
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, ")")
|
|
@ -0,0 +1,71 @@
|
|||
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)
|